This class is used to aggregate the MSODRAWING and OBJ record
combinations. This is necessary due to the bizare way in which
these records are serialized. What happens is that you get a
combination of MSODRAWING -> OBJ -> MSODRAWING -> OBJ records
but the escher records are serialized _across_ the MSODRAWING
records.
It gets even worse when you start looking at TXO records.
So what we do with this class is aggregate lazily. That is
we don't aggregate the MSODRAWING -> OBJ records unless we
need to modify them.
| Field Summary |
|---|
| public static final short | sid | |
| public static final short | ST_MIN | |
| public static final short | ST_NOT_PRIMATIVE | |
| public static final short | ST_RECTANGLE | |
| public static final short | ST_ROUNDRECTANGLE | |
| public static final short | ST_ELLIPSE | |
| public static final short | ST_DIAMOND | |
| public static final short | ST_ISOCELESTRIANGLE | |
| public static final short | ST_RIGHTTRIANGLE | |
| public static final short | ST_PARALLELOGRAM | |
| public static final short | ST_TRAPEZOID | |
| public static final short | ST_HEXAGON | |
| public static final short | ST_OCTAGON | |
| public static final short | ST_PLUS | |
| public static final short | ST_STAR | |
| public static final short | ST_ARROW | |
| public static final short | ST_THICKARROW | |
| public static final short | ST_HOMEPLATE | |
| public static final short | ST_CUBE | |
| public static final short | ST_BALLOON | |
| public static final short | ST_SEAL | |
| public static final short | ST_ARC | |
| public static final short | ST_LINE | |
| public static final short | ST_PLAQUE | |
| public static final short | ST_CAN | |
| public static final short | ST_DONUT | |
| public static final short | ST_TEXTSIMPLE | |
| public static final short | ST_TEXTOCTAGON | |
| public static final short | ST_TEXTHEXAGON | |
| public static final short | ST_TEXTCURVE | |
| public static final short | ST_TEXTWAVE | |
| public static final short | ST_TEXTRING | |
| public static final short | ST_TEXTONCURVE | |
| public static final short | ST_TEXTONRING | |
| public static final short | ST_STRAIGHTCONNECTOR1 | |
| public static final short | ST_BENTCONNECTOR2 | |
| public static final short | ST_BENTCONNECTOR3 | |
| public static final short | ST_BENTCONNECTOR4 | |
| public static final short | ST_BENTCONNECTOR5 | |
| public static final short | ST_CURVEDCONNECTOR2 | |
| public static final short | ST_CURVEDCONNECTOR3 | |
| public static final short | ST_CURVEDCONNECTOR4 | |
| public static final short | ST_CURVEDCONNECTOR5 | |
| public static final short | ST_CALLOUT1 | |
| public static final short | ST_CALLOUT2 | |
| public static final short | ST_CALLOUT3 | |
| public static final short | ST_ACCENTCALLOUT1 | |
| public static final short | ST_ACCENTCALLOUT2 | |
| public static final short | ST_ACCENTCALLOUT3 | |
| public static final short | ST_BORDERCALLOUT1 | |
| public static final short | ST_BORDERCALLOUT2 | |
| public static final short | ST_BORDERCALLOUT3 | |
| public static final short | ST_ACCENTBORDERCALLOUT1 | |
| public static final short | ST_ACCENTBORDERCALLOUT2 | |
| public static final short | ST_ACCENTBORDERCALLOUT3 | |
| public static final short | ST_RIBBON | |
| public static final short | ST_RIBBON2 | |
| public static final short | ST_CHEVRON | |
| public static final short | ST_PENTAGON | |
| public static final short | ST_NOSMOKING | |
| public static final short | ST_SEAL8 | |
| public static final short | ST_SEAL16 | |
| public static final short | ST_SEAL32 | |
| public static final short | ST_WEDGERECTCALLOUT | |
| public static final short | ST_WEDGERRECTCALLOUT | |
| public static final short | ST_WEDGEELLIPSECALLOUT | |
| public static final short | ST_WAVE | |
| public static final short | ST_FOLDEDCORNER | |
| public static final short | ST_LEFTARROW | |
| public static final short | ST_DOWNARROW | |
| public static final short | ST_UPARROW | |
| public static final short | ST_LEFTRIGHTARROW | |
| public static final short | ST_UPDOWNARROW | |
| public static final short | ST_IRREGULARSEAL1 | |
| public static final short | ST_IRREGULARSEAL2 | |
| public static final short | ST_LIGHTNINGBOLT | |
| public static final short | ST_HEART | |
| public static final short | ST_PICTUREFRAME | |
| public static final short | ST_QUADARROW | |
| public static final short | ST_LEFTARROWCALLOUT | |
| public static final short | ST_RIGHTARROWCALLOUT | |
| public static final short | ST_UPARROWCALLOUT | |
| public static final short | ST_DOWNARROWCALLOUT | |
| public static final short | ST_LEFTRIGHTARROWCALLOUT | |
| public static final short | ST_UPDOWNARROWCALLOUT | |
| public static final short | ST_QUADARROWCALLOUT | |
| public static final short | ST_BEVEL | |
| public static final short | ST_LEFTBRACKET | |
| public static final short | ST_RIGHTBRACKET | |
| public static final short | ST_LEFTBRACE | |
| public static final short | ST_RIGHTBRACE | |
| public static final short | ST_LEFTUPARROW | |
| public static final short | ST_BENTUPARROW | |
| public static final short | ST_BENTARROW | |
| public static final short | ST_SEAL24 | |
| public static final short | ST_STRIPEDRIGHTARROW | |
| public static final short | ST_NOTCHEDRIGHTARROW | |
| public static final short | ST_BLOCKARC | |
| public static final short | ST_SMILEYFACE | |
| public static final short | ST_VERTICALSCROLL | |
| public static final short | ST_HORIZONTALSCROLL | |
| public static final short | ST_CIRCULARARROW | |
| public static final short | ST_NOTCHEDCIRCULARARROW | |
| public static final short | ST_UTURNARROW | |
| public static final short | ST_CURVEDRIGHTARROW | |
| public static final short | ST_CURVEDLEFTARROW | |
| public static final short | ST_CURVEDUPARROW | |
| public static final short | ST_CURVEDDOWNARROW | |
| public static final short | ST_CLOUDCALLOUT | |
| public static final short | ST_ELLIPSERIBBON | |
| public static final short | ST_ELLIPSERIBBON2 | |
| public static final short | ST_FLOWCHARTPROCESS | |
| public static final short | ST_FLOWCHARTDECISION | |
| public static final short | ST_FLOWCHARTINPUTOUTPUT | |
| public static final short | ST_FLOWCHARTPREDEFINEDPROCESS | |
| public static final short | ST_FLOWCHARTINTERNALSTORAGE | |
| public static final short | ST_FLOWCHARTDOCUMENT | |
| public static final short | ST_FLOWCHARTMULTIDOCUMENT | |
| public static final short | ST_FLOWCHARTTERMINATOR | |
| public static final short | ST_FLOWCHARTPREPARATION | |
| public static final short | ST_FLOWCHARTMANUALINPUT | |
| public static final short | ST_FLOWCHARTMANUALOPERATION | |
| public static final short | ST_FLOWCHARTCONNECTOR | |
| public static final short | ST_FLOWCHARTPUNCHEDCARD | |
| public static final short | ST_FLOWCHARTPUNCHEDTAPE | |
| public static final short | ST_FLOWCHARTSUMMINGJUNCTION | |
| public static final short | ST_FLOWCHARTOR | |
| public static final short | ST_FLOWCHARTCOLLATE | |
| public static final short | ST_FLOWCHARTSORT | |
| public static final short | ST_FLOWCHARTEXTRACT | |
| public static final short | ST_FLOWCHARTMERGE | |
| public static final short | ST_FLOWCHARTOFFLINESTORAGE | |
| public static final short | ST_FLOWCHARTONLINESTORAGE | |
| public static final short | ST_FLOWCHARTMAGNETICTAPE | |
| public static final short | ST_FLOWCHARTMAGNETICDISK | |
| public static final short | ST_FLOWCHARTMAGNETICDRUM | |
| public static final short | ST_FLOWCHARTDISPLAY | |
| public static final short | ST_FLOWCHARTDELAY | |
| public static final short | ST_TEXTPLAINTEXT | |
| public static final short | ST_TEXTSTOP | |
| public static final short | ST_TEXTTRIANGLE | |
| public static final short | ST_TEXTTRIANGLEINVERTED | |
| public static final short | ST_TEXTCHEVRON | |
| public static final short | ST_TEXTCHEVRONINVERTED | |
| public static final short | ST_TEXTRINGINSIDE | |
| public static final short | ST_TEXTRINGOUTSIDE | |
| public static final short | ST_TEXTARCHUPCURVE | |
| public static final short | ST_TEXTARCHDOWNCURVE | |
| public static final short | ST_TEXTCIRCLECURVE | |
| public static final short | ST_TEXTBUTTONCURVE | |
| public static final short | ST_TEXTARCHUPPOUR | |
| public static final short | ST_TEXTARCHDOWNPOUR | |
| public static final short | ST_TEXTCIRCLEPOUR | |
| public static final short | ST_TEXTBUTTONPOUR | |
| public static final short | ST_TEXTCURVEUP | |
| public static final short | ST_TEXTCURVEDOWN | |
| public static final short | ST_TEXTCASCADEUP | |
| public static final short | ST_TEXTCASCADEDOWN | |
| public static final short | ST_TEXTWAVE1 | |
| public static final short | ST_TEXTWAVE2 | |
| public static final short | ST_TEXTWAVE3 | |
| public static final short | ST_TEXTWAVE4 | |
| public static final short | ST_TEXTINFLATE | |
| public static final short | ST_TEXTDEFLATE | |
| public static final short | ST_TEXTINFLATEBOTTOM | |
| public static final short | ST_TEXTDEFLATEBOTTOM | |
| public static final short | ST_TEXTINFLATETOP | |
| public static final short | ST_TEXTDEFLATETOP | |
| public static final short | ST_TEXTDEFLATEINFLATE | |
| public static final short | ST_TEXTDEFLATEINFLATEDEFLATE | |
| public static final short | ST_TEXTFADERIGHT | |
| public static final short | ST_TEXTFADELEFT | |
| public static final short | ST_TEXTFADEUP | |
| public static final short | ST_TEXTFADEDOWN | |
| public static final short | ST_TEXTSLANTUP | |
| public static final short | ST_TEXTSLANTDOWN | |
| public static final short | ST_TEXTCANUP | |
| public static final short | ST_TEXTCANDOWN | |
| public static final short | ST_FLOWCHARTALTERNATEPROCESS | |
| public static final short | ST_FLOWCHARTOFFPAGECONNECTOR | |
| public static final short | ST_CALLOUT90 | |
| public static final short | ST_ACCENTCALLOUT90 | |
| public static final short | ST_BORDERCALLOUT90 | |
| public static final short | ST_ACCENTBORDERCALLOUT90 | |
| public static final short | ST_LEFTRIGHTUPARROW | |
| public static final short | ST_SUN | |
| public static final short | ST_MOON | |
| public static final short | ST_BRACKETPAIR | |
| public static final short | ST_BRACEPAIR | |
| public static final short | ST_SEAL4 | |
| public static final short | ST_DOUBLEWAVE | |
| public static final short | ST_ACTIONBUTTONBLANK | |
| public static final short | ST_ACTIONBUTTONHOME | |
| public static final short | ST_ACTIONBUTTONHELP | |
| public static final short | ST_ACTIONBUTTONINFORMATION | |
| public static final short | ST_ACTIONBUTTONFORWARDNEXT | |
| public static final short | ST_ACTIONBUTTONBACKPREVIOUS | |
| public static final short | ST_ACTIONBUTTONEND | |
| public static final short | ST_ACTIONBUTTONBEGINNING | |
| public static final short | ST_ACTIONBUTTONRETURN | |
| public static final short | ST_ACTIONBUTTONDOCUMENT | |
| public static final short | ST_ACTIONBUTTONSOUND | |
| public static final short | ST_ACTIONBUTTONMOVIE | |
| public static final short | ST_HOSTCONTROL | |
| public static final short | ST_TEXTBOX | |
| public static final short | ST_NIL | |
| protected HSSFPatriarch | patriarch | |
| Method from org.apache.poi.hssf.record.EscherAggregate Summary: |
|---|
|
assoicateShapeToObjRecord, clear, convertRecordsToUserModel, createAggregate, fillFields, getPatriarch, getRecordName, getRecordSize, getSid, serialize, setPatriarch, toString |
| Methods from org.apache.poi.hssf.record.AbstractEscherHolderRecord: |
|---|
|
addEscherRecord, addEscherRecord, clearEscherRecords, clone, convertRawBytesToEscherRecords, decode, fillFields, findFirstWithId, getEscherContainer, getEscherRecord, getEscherRecords, getRawData, getRecordName, getRecordSize, getSid, join, processContinueRecord, serialize, setRawData, toString, validateSid |
| Methods from org.apache.poi.hssf.record.Record: |
|---|
|
clone, cloneViaReserialise, fillFields, getRecordSize, getSid, isInValueSection, isValue, serialize, serialize, toString, validateSid |
| Method from org.apache.poi.hssf.record.EscherAggregate Detail: |
public Object assoicateShapeToObjRecord(EscherRecord r,
Record objRecord) {
return shapeToObj.put( r, objRecord );
}
Associates an escher record to an OBJ record or a TXO record. |
public void clear() {
clearEscherRecords();
shapeToObj.clear();
// lastShapeId = 1024;
}
|
public void convertRecordsToUserModel() {
if(patriarch == null) {
throw new IllegalStateException("Must call setPatriarch() first");
}
// The top level container ought to have
// the DgRecord and the container of one container
// per shape group (patriach overall first)
EscherContainerRecord topContainer =
(EscherContainerRecord)getEscherContainer();
if(topContainer == null) {
return;
}
topContainer = (EscherContainerRecord)
topContainer.getChildContainers().get(0);
List tcc = topContainer.getChildContainers();
if(tcc.size() == 0) {
throw new IllegalStateException("No child escher containers at the point that should hold the patriach data, and one container per top level shape!");
}
// First up, get the patriach position
// This is in the first EscherSpgrRecord, in
// the first container, with a EscherSRecord too
EscherContainerRecord patriachContainer =
(EscherContainerRecord)tcc.get(0);
EscherSpgrRecord spgr = null;
for(Iterator it = patriachContainer.getChildRecords().iterator(); it.hasNext();) {
EscherRecord r = (EscherRecord)it.next();
if(r instanceof EscherSpgrRecord) {
spgr = (EscherSpgrRecord)r;
break;
}
}
if(spgr != null) {
patriarch.setCoordinates(
spgr.getRectX1(), spgr.getRectY1(),
spgr.getRectX2(), spgr.getRectY2()
);
}
// Now process the containers for each group
// and objects
for(int i=1; i< tcc.size(); i++) {
EscherContainerRecord shapeContainer =
(EscherContainerRecord)tcc.get(i);
//System.err.println("\n\n*****\n\n");
//System.err.println(shapeContainer);
// Could be a group, or a base object
if(shapeContainer.getChildRecords().size() == 1 &&
shapeContainer.getChildContainers().size() == 1) {
// Group
HSSFShapeGroup group =
new HSSFShapeGroup(null, new HSSFClientAnchor());
patriarch.getChildren().add(group);
EscherContainerRecord groupContainer =
(EscherContainerRecord)shapeContainer.getChild(0);
convertRecordsToUserModel(groupContainer, group);
} else if(shapeContainer.hasChildOfType((short)0xF00D)) {
// TextBox
HSSFTextbox box =
new HSSFTextbox(null, new HSSFClientAnchor());
patriarch.getChildren().add(box);
convertRecordsToUserModel(shapeContainer, box);
} else if(shapeContainer.hasChildOfType((short)0xF011)) {
// Not yet supporting EscherClientDataRecord stuff
} else {
// Base level
convertRecordsToUserModel(shapeContainer, patriarch);
}
}
// Now, clear any trace of what records make up
// the patriarch
// Otherwise, everything will go horribly wrong
// when we try to write out again....
// clearEscherRecords();
drawingManager.getDgg().setFileIdClusters(new EscherDggRecord.FileIdCluster[0]);
// TODO: Support converting our records
// back into shapes
log.log(POILogger.WARN, "Not processing objects into Patriarch!");
}
Converts the Records into UserModel
objects on the bound HSSFPatriarch |
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.
final List shapeRecords = new ArrayList();
EscherRecordFactory recordFactory = new DefaultEscherRecordFactory()
{
public EscherRecord createRecord( byte[] data, int offset )
{
EscherRecord r = super.createRecord( data, offset );
if ( r.getRecordId() == EscherClientDataRecord.RECORD_ID || r.getRecordId() == EscherTextboxRecord.RECORD_ID )
{
shapeRecords.add( r );
}
return r;
}
};
// Calculate the size of the buffer
EscherAggregate agg = new EscherAggregate(drawingManager);
int loc = locFirstDrawingRecord;
int dataSize = 0;
while ( loc + 1 < records.size()
&& sid( records, loc ) == DrawingRecord.sid
&& isObjectRecord( records, loc + 1 ) )
{
dataSize += ( (DrawingRecord) records.get( loc ) ).getData().length;
loc += 2;
}
// Create one big buffer
byte buffer[] = new byte[dataSize];
int offset = 0;
loc = locFirstDrawingRecord;
while ( loc + 1 < records.size()
&& sid( records, loc ) == DrawingRecord.sid
&& isObjectRecord( records, loc + 1 ) )
{
DrawingRecord drawingRecord = (DrawingRecord) records.get( loc );
System.arraycopy( drawingRecord.getData(), 0, buffer, offset, drawingRecord.getData().length );
offset += drawingRecord.getData().length;
loc += 2;
}
// Decode the shapes
// agg.escherRecords = new ArrayList();
int pos = 0;
while ( pos < dataSize )
{
EscherRecord r = recordFactory.createRecord( buffer, pos );
int bytesRead = r.fillFields( buffer, pos, recordFactory );
agg.addEscherRecord( r );
pos += bytesRead;
}
// Associate the object records with the shapes
loc = locFirstDrawingRecord;
int shapeIndex = 0;
agg.shapeToObj = new HashMap();
while ( loc + 1 < records.size()
&& sid( records, loc ) == DrawingRecord.sid
&& isObjectRecord( records, loc + 1 ) )
{
Record objRecord = (Record) records.get( loc + 1 );
agg.shapeToObj.put( shapeRecords.get( shapeIndex++ ), objRecord );
loc += 2;
}
return agg;
}
Collapses the drawing records into an aggregate. |
protected void fillFields(byte[] data,
short size,
int offset) {
throw new IllegalStateException( "Should not reach here" );
}
Unused since this is an aggregate record. Use createAggregate(). |
public HSSFPatriarch getPatriarch() {
return patriarch;
}
|
protected String getRecordName() {
return "ESCHERAGGREGATE";
}
|
public int getRecordSize() {
convertUserModelToRecords();
List records = getEscherRecords();
int rawEscherSize = getEscherRecordSize( records );
int drawingRecordSize = rawEscherSize + ( shapeToObj.size() ) * 4;
int objRecordSize = 0;
for ( Iterator iterator = shapeToObj.values().iterator(); iterator.hasNext(); )
{
Record r = (Record) iterator.next();
objRecordSize += r.getRecordSize();
}
int tailRecordSize = 0;
for ( Iterator iterator = tailRec.iterator(); iterator.hasNext(); )
{
Record r = (Record) iterator.next();
tailRecordSize += r.getRecordSize();
}
return drawingRecordSize + objRecordSize + tailRecordSize;
}
The number of bytes required to serialize this record. |
public short getSid() {
return sid;
}
|
public int serialize(int offset,
byte[] data) {
convertUserModelToRecords();
// Determine buffer size
List records = getEscherRecords();
int size = getEscherRecordSize( records );
byte[] buffer = new byte[size];
// Serialize escher records into one big data structure and keep note of ending offsets.
final List spEndingOffsets = new ArrayList();
final List shapes = new ArrayList();
int pos = 0;
for ( Iterator iterator = records.iterator(); iterator.hasNext(); )
{
EscherRecord e = (EscherRecord) iterator.next();
pos += e.serialize( pos, buffer, new EscherSerializationListener()
{
public void beforeRecordSerialize( int offset, short recordId, EscherRecord record )
{
}
public void afterRecordSerialize( int offset, short recordId, int size, EscherRecord record )
{
if ( recordId == EscherClientDataRecord.RECORD_ID || recordId == EscherTextboxRecord.RECORD_ID )
{
spEndingOffsets.add( new Integer( offset ) );
shapes.add( record );
}
}
} );
}
// todo: fix this
shapes.add( 0, null );
spEndingOffsets.add( 0, null );
// Split escher records into separate MSODRAWING and OBJ, TXO records. (We don't break on
// the first one because it's the patriach).
pos = offset;
for ( int i = 1; i < shapes.size(); i++ )
{
int endOffset = ( (Integer) spEndingOffsets.get( i ) ).intValue() - 1;
int startOffset;
if ( i == 1 )
startOffset = 0;
else
startOffset = ( (Integer) spEndingOffsets.get( i - 1 ) ).intValue();
// Create and write a new MSODRAWING record
DrawingRecord drawing = new DrawingRecord();
byte[] drawingData = new byte[endOffset - startOffset + 1];
System.arraycopy( buffer, startOffset, drawingData, 0, drawingData.length );
drawing.setData( drawingData );
int temp = drawing.serialize( pos, data );
pos += temp;
// Write the matching OBJ record
Record obj = (Record) shapeToObj.get( shapes.get( i ) );
temp = obj.serialize( pos, data );
pos += temp;
}
// write records that need to be serialized after all drawing group records
for ( int i = 0; i < tailRec.size(); i++ )
{
Record rec = (Record)tailRec.get(i);
pos += rec.serialize( pos, data );
}
int bytesWritten = pos - offset;
if ( bytesWritten != getRecordSize() )
throw new RecordFormatException( bytesWritten + " bytes written but getRecordSize() reports " + getRecordSize() );
return bytesWritten;
}
Serializes this aggregate to a byte array. Since this is an aggregate
record it will effectively serialize the aggregated records. |
public void setPatriarch(HSSFPatriarch patriarch) {
this.patriarch = patriarch;
}
|
public String toString() {
String nl = System.getProperty( "line.separtor" );
StringBuffer result = new StringBuffer();
result.append( '[" ).append( getRecordName() ).append( ']" + nl );
for ( Iterator iterator = getEscherRecords().iterator(); iterator.hasNext(); )
{
EscherRecord escherRecord = (EscherRecord) iterator.next();
result.append( escherRecord.toString() );
}
result.append( "[/" ).append( getRecordName() ).append( ']" + nl );
return result.toString();
}
Calculates the string representation of this record. This is
simply a dump of all the records. |