git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1871563 13f79535-47bb-0310-9956-ffa450edef68tags/REL_4_1_2
@@ -30,13 +30,20 @@ import org.apache.poi.util.Removal; | |||
* Common abstract class for {@link EscherOptRecord} and | |||
* {@link EscherTertiaryOptRecord} | |||
*/ | |||
public abstract class AbstractEscherOptRecord extends EscherRecord | |||
{ | |||
public abstract class AbstractEscherOptRecord extends EscherRecord { | |||
private final List<EscherProperty> properties = new ArrayList<>(); | |||
protected AbstractEscherOptRecord() {} | |||
protected AbstractEscherOptRecord(AbstractEscherOptRecord other) { | |||
super(other); | |||
properties.addAll(other.properties); | |||
} | |||
/** | |||
* Add a property to this record. | |||
* | |||
* | |||
* @param prop the escher property to add | |||
*/ | |||
public void addEscherProperty( EscherProperty prop ) | |||
@@ -60,7 +67,7 @@ public abstract class AbstractEscherOptRecord extends EscherRecord | |||
/** | |||
* The list of properties stored by this record. | |||
* | |||
* | |||
* @return the list of properties | |||
*/ | |||
public List<EscherProperty> getEscherProperties() | |||
@@ -70,7 +77,7 @@ public abstract class AbstractEscherOptRecord extends EscherRecord | |||
/** | |||
* The list of properties stored by this record. | |||
* | |||
* | |||
* @param index the ordinal index of the property | |||
* @return the escher property | |||
*/ | |||
@@ -79,7 +86,7 @@ public abstract class AbstractEscherOptRecord extends EscherRecord | |||
return properties.get( index ); | |||
} | |||
private int getPropertiesSize() | |||
{ | |||
int totalSize = 0; | |||
@@ -164,4 +171,7 @@ public abstract class AbstractEscherOptRecord extends EscherRecord | |||
"properties", this::getEscherProperties | |||
); | |||
} | |||
@Override | |||
public abstract AbstractEscherOptRecord copy(); | |||
} |
@@ -58,7 +58,25 @@ public final class EscherBSERecord extends EscherRecord { | |||
public EscherBSERecord() { | |||
setRecordId(RECORD_ID); | |||
} | |||
public EscherBSERecord(EscherBSERecord other) { | |||
super(other); | |||
field_1_blipTypeWin32 = other.field_1_blipTypeWin32; | |||
field_2_blipTypeMacOS = other.field_2_blipTypeMacOS; | |||
System.arraycopy(other.field_3_uid, 0, field_3_uid, 0, field_3_uid.length); | |||
field_4_tag = other.field_4_tag; | |||
field_5_size = other.field_5_size; | |||
field_6_ref = other.field_6_ref; | |||
field_7_offset = other.field_7_offset; | |||
field_8_usage = other.field_8_usage; | |||
field_9_name = other.field_9_name; | |||
field_10_unused2 = other.field_10_unused2; | |||
field_11_unused3 = other.field_11_unused3; | |||
field_12_blipRecord = other.field_12_blipRecord.copy(); | |||
_remainingData = other._remainingData.clone(); | |||
} | |||
@Override | |||
public int fillFields(byte[] data, int offset, EscherRecordFactory recordFactory) { | |||
int bytesRemaining = readHeader( data, offset ); | |||
@@ -149,7 +167,7 @@ public final class EscherBSERecord extends EscherRecord { | |||
/** | |||
* The expected blip type under windows (failure to match this blip type will result in | |||
* Excel converting to this format). | |||
* | |||
* | |||
* @return win32 blip type | |||
*/ | |||
public byte getBlipTypeWin32() { | |||
@@ -162,7 +180,7 @@ public final class EscherBSERecord extends EscherRecord { | |||
/** | |||
* Set the expected win32 blip type | |||
* | |||
* | |||
* @param blipTypeWin32 win32 blip type | |||
*/ | |||
public void setBlipTypeWin32(byte blipTypeWin32) { | |||
@@ -172,7 +190,7 @@ public final class EscherBSERecord extends EscherRecord { | |||
/** | |||
* The expected blip type under MacOS (failure to match this blip type will result in | |||
* Excel converting to this format). | |||
* | |||
* | |||
* @return MacOS blip type | |||
*/ | |||
public byte getBlipTypeMacOS() { | |||
@@ -185,7 +203,7 @@ public final class EscherBSERecord extends EscherRecord { | |||
/** | |||
* Set the expected MacOS blip type | |||
* | |||
* | |||
* @param blipTypeMacOS MacOS blip type | |||
*/ | |||
public void setBlipTypeMacOS(byte blipTypeMacOS) { | |||
@@ -194,7 +212,7 @@ public final class EscherBSERecord extends EscherRecord { | |||
/** | |||
* 16 byte MD4 checksum. | |||
* | |||
* | |||
* @return 16 byte MD4 checksum | |||
*/ | |||
public byte[] getUid() { | |||
@@ -203,7 +221,7 @@ public final class EscherBSERecord extends EscherRecord { | |||
/** | |||
* 16 byte MD4 checksum. | |||
* | |||
* | |||
* @param uid 16 byte MD4 checksum | |||
*/ | |||
public void setUid(byte[] uid) { | |||
@@ -215,7 +233,7 @@ public final class EscherBSERecord extends EscherRecord { | |||
/** | |||
* unused | |||
* | |||
* | |||
* @return an unknown tag | |||
*/ | |||
public short getTag() { | |||
@@ -224,7 +242,7 @@ public final class EscherBSERecord extends EscherRecord { | |||
/** | |||
* unused | |||
* | |||
* | |||
* @param tag unknown tag | |||
*/ | |||
public void setTag(short tag) { | |||
@@ -233,7 +251,7 @@ public final class EscherBSERecord extends EscherRecord { | |||
/** | |||
* Blip size in stream. | |||
* | |||
* | |||
* @return the blip size | |||
*/ | |||
public int getSize() { | |||
@@ -242,7 +260,7 @@ public final class EscherBSERecord extends EscherRecord { | |||
/** | |||
* Blip size in stream. | |||
* | |||
* | |||
* @param size blip size | |||
*/ | |||
public void setSize(int size) { | |||
@@ -251,7 +269,7 @@ public final class EscherBSERecord extends EscherRecord { | |||
/** | |||
* The reference count of this blip. | |||
* | |||
* | |||
* @return the reference count | |||
*/ | |||
public int getRef() { | |||
@@ -260,7 +278,7 @@ public final class EscherBSERecord extends EscherRecord { | |||
/** | |||
* The reference count of this blip. | |||
* | |||
* | |||
* @param ref the reference count | |||
*/ | |||
public void setRef(int ref) { | |||
@@ -269,7 +287,7 @@ public final class EscherBSERecord extends EscherRecord { | |||
/** | |||
* File offset in the delay stream. | |||
* | |||
* | |||
* @return the file offset | |||
*/ | |||
public int getOffset() { | |||
@@ -278,7 +296,7 @@ public final class EscherBSERecord extends EscherRecord { | |||
/** | |||
* File offset in the delay stream. | |||
* | |||
* | |||
* @param offset the file offset | |||
*/ | |||
public void setOffset(int offset) { | |||
@@ -287,7 +305,7 @@ public final class EscherBSERecord extends EscherRecord { | |||
/** | |||
* Defines the way this blip is used. | |||
* | |||
* | |||
* @return the blip usage | |||
*/ | |||
public byte getUsage() { | |||
@@ -296,7 +314,7 @@ public final class EscherBSERecord extends EscherRecord { | |||
/** | |||
* Defines the way this blip is used. | |||
* | |||
* | |||
* @param usage the blip usae | |||
*/ | |||
public void setUsage(byte usage) { | |||
@@ -305,7 +323,7 @@ public final class EscherBSERecord extends EscherRecord { | |||
/** | |||
* The length in characters of the blip name. | |||
* | |||
* | |||
* @return the blip name length | |||
*/ | |||
public byte getName() { | |||
@@ -314,7 +332,7 @@ public final class EscherBSERecord extends EscherRecord { | |||
/** | |||
* The length in characters of the blip name. | |||
* | |||
* | |||
* @param name the blip name length | |||
*/ | |||
public void setName(byte name) { | |||
@@ -347,7 +365,7 @@ public final class EscherBSERecord extends EscherRecord { | |||
/** | |||
* Any remaining data in this record. | |||
* | |||
* | |||
* @return the remaining bytes | |||
*/ | |||
public byte[] getRemainingData() { | |||
@@ -356,7 +374,7 @@ public final class EscherBSERecord extends EscherRecord { | |||
/** | |||
* Any remaining data in this record. | |||
* | |||
* | |||
* @param remainingData the remaining bytes | |||
*/ | |||
public void setRemainingData(byte[] remainingData) { | |||
@@ -388,4 +406,9 @@ public final class EscherBSERecord extends EscherRecord { | |||
public Enum getGenericRecordType() { | |||
return EscherRecordTypes.BSE; | |||
} | |||
@Override | |||
public EscherBSERecord copy() { | |||
return new EscherBSERecord(this); | |||
} | |||
} |
@@ -33,6 +33,14 @@ public class EscherBitmapBlip extends EscherBlipRecord { | |||
private final byte[] field_1_UID = new byte[16]; | |||
private byte field_2_marker = (byte) 0xFF; | |||
public EscherBitmapBlip() {} | |||
public EscherBitmapBlip(EscherBitmapBlip other) { | |||
super(other); | |||
System.arraycopy(other.field_1_UID, 0, field_1_UID, 0, field_1_UID.length); | |||
field_2_marker = other.field_2_marker; | |||
} | |||
@Override | |||
public int fillFields(byte[] data, int offset, EscherRecordFactory recordFactory) { | |||
int bytesAfterHeader = readHeader( data, offset ); | |||
@@ -121,4 +129,8 @@ public class EscherBitmapBlip extends EscherBlipRecord { | |||
); | |||
} | |||
@Override | |||
public EscherBitmapBlip copy() { | |||
return new EscherBitmapBlip(this); | |||
} | |||
} |
@@ -39,6 +39,11 @@ public class EscherBlipRecord extends EscherRecord { | |||
public EscherBlipRecord() { | |||
} | |||
public EscherBlipRecord(EscherBlipRecord other) { | |||
super(other); | |||
field_pictureData = (other.field_pictureData == null) ? null : other.field_pictureData.clone(); | |||
} | |||
@Override | |||
public int fillFields(byte[] data, int offset, EscherRecordFactory recordFactory) { | |||
int bytesAfterHeader = readHeader( data, offset ); | |||
@@ -120,4 +125,10 @@ public class EscherBlipRecord extends EscherRecord { | |||
EscherRecordTypes t = EscherRecordTypes.forTypeID(getRecordId()); | |||
return (t != EscherRecordTypes.UNKNOWN) ? t : EscherRecordTypes.BLIP_START; | |||
} | |||
@Override | |||
public EscherBlipRecord copy() { | |||
return new EscherBlipRecord(this); | |||
} | |||
} |
@@ -38,6 +38,16 @@ public class EscherChildAnchorRecord extends EscherRecord { | |||
private int field_3_dx2; | |||
private int field_4_dy2; | |||
public EscherChildAnchorRecord() {} | |||
public EscherChildAnchorRecord(EscherChildAnchorRecord other) { | |||
super(other); | |||
field_1_dx1 = other.field_1_dx1; | |||
field_2_dy1 = other.field_2_dy1; | |||
field_3_dx2 = other.field_3_dx2; | |||
field_4_dy2 = other.field_4_dy2; | |||
} | |||
@Override | |||
public int fillFields(byte[] data, int offset, EscherRecordFactory recordFactory) { | |||
int bytesRemaining = readHeader( data, offset ); | |||
@@ -59,7 +69,7 @@ public class EscherChildAnchorRecord extends EscherRecord { | |||
default: | |||
throw new RuntimeException("Invalid EscherChildAnchorRecord - neither 8 nor 16 bytes."); | |||
} | |||
return 8 + size; | |||
} | |||
@@ -98,7 +108,7 @@ public class EscherChildAnchorRecord extends EscherRecord { | |||
/** | |||
* Retrieves offset within the parent coordinate space for the top left point. | |||
* | |||
* | |||
* @return the x offset of the top left point | |||
*/ | |||
public int getDx1() | |||
@@ -108,7 +118,7 @@ public class EscherChildAnchorRecord extends EscherRecord { | |||
/** | |||
* Sets offset within the parent coordinate space for the top left point. | |||
* | |||
* | |||
* @param field_1_dx1 the x offset of the top left point | |||
*/ | |||
public void setDx1( int field_1_dx1 ) | |||
@@ -118,7 +128,7 @@ public class EscherChildAnchorRecord extends EscherRecord { | |||
/** | |||
* Gets offset within the parent coordinate space for the top left point. | |||
* | |||
* | |||
* @return the y offset of the top left point | |||
*/ | |||
public int getDy1() | |||
@@ -128,8 +138,8 @@ public class EscherChildAnchorRecord extends EscherRecord { | |||
/** | |||
* Sets offset within the parent coordinate space for the top left point. | |||
* | |||
* @param field_2_dy1 the y offset of the top left point | |||
* | |||
* @param field_2_dy1 the y offset of the top left point | |||
*/ | |||
public void setDy1( int field_2_dy1 ) | |||
{ | |||
@@ -138,7 +148,7 @@ public class EscherChildAnchorRecord extends EscherRecord { | |||
/** | |||
* Retrieves offset within the parent coordinate space for the bottom right point. | |||
* | |||
* | |||
* @return the x offset of the bottom right point | |||
*/ | |||
public int getDx2() | |||
@@ -148,7 +158,7 @@ public class EscherChildAnchorRecord extends EscherRecord { | |||
/** | |||
* Sets offset within the parent coordinate space for the bottom right point. | |||
* | |||
* | |||
* @param field_3_dx2 the x offset of the bottom right point | |||
*/ | |||
public void setDx2( int field_3_dx2 ) | |||
@@ -158,7 +168,7 @@ public class EscherChildAnchorRecord extends EscherRecord { | |||
/** | |||
* Gets the offset within the parent coordinate space for the bottom right point. | |||
* | |||
* | |||
* @return the y offset of the bottom right point | |||
*/ | |||
public int getDy2() | |||
@@ -168,7 +178,7 @@ public class EscherChildAnchorRecord extends EscherRecord { | |||
/** | |||
* Sets the offset within the parent coordinate space for the bottom right point. | |||
* | |||
* | |||
* @param field_4_dy2 the y offset of the bottom right point | |||
*/ | |||
public void setDy2( int field_4_dy2 ) | |||
@@ -191,4 +201,9 @@ public class EscherChildAnchorRecord extends EscherRecord { | |||
public Enum getGenericRecordType() { | |||
return EscherRecordTypes.CHILD_ANCHOR; | |||
} | |||
@Override | |||
public EscherChildAnchorRecord copy() { | |||
return new EscherChildAnchorRecord(this); | |||
} | |||
} |
@@ -59,6 +59,23 @@ public class EscherClientAnchorRecord extends EscherRecord { | |||
private byte[] remainingData = new byte[0]; | |||
private boolean shortRecord; | |||
public EscherClientAnchorRecord() {} | |||
public EscherClientAnchorRecord(EscherClientAnchorRecord other) { | |||
super(other); | |||
field_1_flag = other.field_1_flag; | |||
field_2_col1 = other.field_2_col1; | |||
field_3_dx1 = other.field_3_dx1; | |||
field_4_row1 = other.field_4_row1; | |||
field_5_dy1 = other.field_5_dy1; | |||
field_6_col2 = other.field_6_col2; | |||
field_7_dx2 = other.field_7_dx2; | |||
field_8_row2 = other.field_8_row2; | |||
field_9_dy2 = other.field_9_dy2; | |||
remainingData = (other.remainingData == null) ? null : other.remainingData.clone(); | |||
shortRecord = other.shortRecord; | |||
} | |||
@Override | |||
public int fillFields(byte[] data, int offset, EscherRecordFactory recordFactory) { | |||
int bytesRemaining = readHeader( data, offset ); | |||
@@ -368,4 +385,9 @@ public class EscherClientAnchorRecord extends EscherRecord { | |||
public Enum getGenericRecordType() { | |||
return EscherRecordTypes.CLIENT_ANCHOR; | |||
} | |||
@Override | |||
public EscherClientAnchorRecord copy() { | |||
return new EscherClientAnchorRecord(this); | |||
} | |||
} |
@@ -29,9 +29,7 @@ import org.apache.poi.util.LittleEndian; | |||
* The EscherClientDataRecord is used to store client specific data about the position of a | |||
* shape within a container. | |||
*/ | |||
public class EscherClientDataRecord | |||
extends EscherRecord | |||
{ | |||
public class EscherClientDataRecord extends EscherRecord { | |||
//arbitrarily selected; may need to increase | |||
private static final int MAX_RECORD_LENGTH = 100_000; | |||
@@ -39,6 +37,13 @@ public class EscherClientDataRecord | |||
private byte[] remainingData; | |||
public EscherClientDataRecord() {} | |||
public EscherClientDataRecord(EscherClientDataRecord other) { | |||
super(other); | |||
remainingData = (other.remainingData == null) ? null : other.remainingData.clone(); | |||
} | |||
@Override | |||
public int fillFields(byte[] data, int offset, EscherRecordFactory recordFactory) { | |||
int bytesRemaining = readHeader( data, offset ); | |||
@@ -83,7 +88,7 @@ public class EscherClientDataRecord | |||
/** | |||
* Any data recording this record. | |||
* | |||
* | |||
* @return the remaining bytes | |||
*/ | |||
public byte[] getRemainingData() | |||
@@ -93,7 +98,7 @@ public class EscherClientDataRecord | |||
/** | |||
* Any data recording this record. | |||
* | |||
* | |||
* @param remainingData the remaining bytes | |||
*/ | |||
public void setRemainingData( byte[] remainingData ) { | |||
@@ -114,4 +119,9 @@ public class EscherClientDataRecord | |||
public Enum getGenericRecordType() { | |||
return EscherRecordTypes.CLIENT_DATA; | |||
} | |||
@Override | |||
public EscherClientDataRecord copy() { | |||
return new EscherClientDataRecord(this); | |||
} | |||
} |
@@ -73,6 +73,14 @@ public final class EscherContainerRecord extends EscherRecord implements Iterabl | |||
private final List<EscherRecord> _childRecords = new ArrayList<>(); | |||
public EscherContainerRecord() {} | |||
public EscherContainerRecord(EscherContainerRecord other) { | |||
super(other); | |||
_remainingLength = other._remainingLength; | |||
other._childRecords.stream().map(EscherRecord::copy).forEach(_childRecords::add); | |||
} | |||
@Override | |||
public int fillFields(byte[] data, int pOffset, EscherRecordFactory recordFactory) { | |||
int bytesRemaining = readHeader(data, pOffset); | |||
@@ -284,4 +292,9 @@ public final class EscherContainerRecord extends EscherRecord implements Iterabl | |||
public Enum getGenericRecordType() { | |||
return EscherRecordTypes.forTypeID(getRecordId()); | |||
} | |||
@Override | |||
public EscherContainerRecord copy() { | |||
return new EscherContainerRecord(this); | |||
} | |||
} |
@@ -34,6 +34,15 @@ public class EscherDgRecord extends EscherRecord { | |||
private int field_1_numShapes; | |||
private int field_2_lastMSOSPID; | |||
public EscherDgRecord() {} | |||
public EscherDgRecord(EscherDgRecord other) { | |||
super(other); | |||
field_1_numShapes = other.field_1_numShapes; | |||
field_2_lastMSOSPID = other.field_2_lastMSOSPID; | |||
} | |||
@Override | |||
public int fillFields(byte[] data, int offset, EscherRecordFactory recordFactory) { | |||
/*int bytesRemaining =*/ readHeader( data, offset ); | |||
@@ -87,7 +96,7 @@ public class EscherDgRecord extends EscherRecord { | |||
/** | |||
* The number of shapes in this drawing group. | |||
* | |||
* | |||
* @return the number of shapes | |||
*/ | |||
public int getNumShapes() | |||
@@ -97,7 +106,7 @@ public class EscherDgRecord extends EscherRecord { | |||
/** | |||
* The number of shapes in this drawing group. | |||
* | |||
* | |||
* @param field_1_numShapes the number of shapes | |||
*/ | |||
public void setNumShapes( int field_1_numShapes ) | |||
@@ -107,7 +116,7 @@ public class EscherDgRecord extends EscherRecord { | |||
/** | |||
* The last shape id used in this drawing group. | |||
* | |||
* | |||
* @return the last shape id | |||
*/ | |||
public int getLastMSOSPID() | |||
@@ -117,7 +126,7 @@ public class EscherDgRecord extends EscherRecord { | |||
/** | |||
* The last shape id used in this drawing group. | |||
* | |||
* | |||
* @param field_2_lastMSOSPID the last shape id | |||
*/ | |||
public void setLastMSOSPID( int field_2_lastMSOSPID ) | |||
@@ -158,4 +167,9 @@ public class EscherDgRecord extends EscherRecord { | |||
public Enum getGenericRecordType() { | |||
return EscherRecordTypes.DG; | |||
} | |||
@Override | |||
public EscherDgRecord copy() { | |||
return new EscherDgRecord(this); | |||
} | |||
} |
@@ -20,7 +20,6 @@ package org.apache.poi.ddf; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.BitSet; | |||
import java.util.Comparator; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.function.Supplier; | |||
@@ -48,6 +47,11 @@ public final class EscherDggRecord extends EscherRecord { | |||
private int field_1_drawingGroupId; | |||
private int field_2_numShapeIdsUsed; | |||
public FileIdCluster(FileIdCluster other) { | |||
field_1_drawingGroupId = other.field_1_drawingGroupId; | |||
field_2_numShapeIdsUsed = other.field_2_numShapeIdsUsed; | |||
} | |||
public FileIdCluster( int drawingGroupId, int numShapeIdsUsed ) { | |||
this.field_1_drawingGroupId = drawingGroupId; | |||
this.field_2_numShapeIdsUsed = numShapeIdsUsed; | |||
@@ -65,6 +69,12 @@ public final class EscherDggRecord extends EscherRecord { | |||
field_2_numShapeIdsUsed++; | |||
} | |||
private static int compareFileIdCluster(FileIdCluster f1, FileIdCluster f2) { | |||
int dgDif = f1.getDrawingGroupId() - f2.getDrawingGroupId(); | |||
int cntDif = f2.getNumShapeIdsUsed() - f1.getNumShapeIdsUsed(); | |||
return (dgDif != 0) ? dgDif : cntDif; | |||
} | |||
@Override | |||
public Map<String, Supplier<?>> getGenericProperties() { | |||
return GenericRecordUtil.getGenericProperties( | |||
@@ -74,6 +84,17 @@ public final class EscherDggRecord extends EscherRecord { | |||
} | |||
} | |||
public EscherDggRecord() {} | |||
public EscherDggRecord(EscherDggRecord other) { | |||
super(other); | |||
field_1_shapeIdMax = other.field_1_shapeIdMax; | |||
field_3_numShapesSaved = other.field_3_numShapesSaved; | |||
field_4_drawingsSaved = other.field_4_drawingsSaved; | |||
other.field_5_fileIdClusters.stream().map(FileIdCluster::new).forEach(field_5_fileIdClusters::add); | |||
maxDgId = other.maxDgId; | |||
} | |||
@Override | |||
public int fillFields(byte[] data, int offset, EscherRecordFactory recordFactory) { | |||
int bytesRemaining = readHeader( data, offset ); | |||
@@ -81,14 +102,14 @@ public final class EscherDggRecord extends EscherRecord { | |||
int size = 0; | |||
field_1_shapeIdMax = LittleEndian.getInt( data, pos + size );size+=4; | |||
// field_2_numIdClusters = LittleEndian.getInt( data, pos + size ); | |||
size+=4; | |||
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.clear(); | |||
// Can't rely on field_2_numIdClusters | |||
int numIdClusters = (bytesRemaining-size) / 8; | |||
for (int i = 0; i < numIdClusters; i++) { | |||
int drawingGroupId = LittleEndian.getInt( data, pos + size ); | |||
int numShapeIdsUsed = LittleEndian.getInt( data, pos + size + 4 ); | |||
@@ -118,7 +139,7 @@ public final class EscherDggRecord extends EscherRecord { | |||
LittleEndian.putInt( data, pos, getNumIdClusters() ); pos += 4; | |||
LittleEndian.putInt( data, pos, field_3_numShapesSaved ); pos += 4; | |||
LittleEndian.putInt( data, pos, field_4_drawingsSaved ); pos += 4; | |||
for (FileIdCluster fic : field_5_fileIdClusters) { | |||
LittleEndian.putInt( data, pos, fic.getDrawingGroupId() ); pos += 4; | |||
LittleEndian.putInt( data, pos, fic.getNumShapeIdsUsed() ); pos += 4; | |||
@@ -154,7 +175,7 @@ public final class EscherDggRecord extends EscherRecord { | |||
/** | |||
* The maximum is actually the next available shape id. | |||
* | |||
* | |||
* @param shapeIdMax the next available shape id | |||
*/ | |||
public void setShapeIdMax(int shapeIdMax) { | |||
@@ -163,7 +184,7 @@ public final class EscherDggRecord extends EscherRecord { | |||
/** | |||
* Number of id clusters + 1 | |||
* | |||
* | |||
* @return the number of id clusters + 1 | |||
*/ | |||
public int getNumIdClusters() { | |||
@@ -181,7 +202,7 @@ public final class EscherDggRecord extends EscherRecord { | |||
/** | |||
* Sets the number of shapes saved | |||
* | |||
* | |||
* @param numShapesSaved the number of shapes saved | |||
*/ | |||
public void setNumShapesSaved(int numShapesSaved) { | |||
@@ -208,7 +229,7 @@ public final class EscherDggRecord extends EscherRecord { | |||
/** | |||
* Gets the maximum drawing group ID | |||
* | |||
* | |||
* @return The maximum drawing group ID | |||
*/ | |||
public int getMaxDrawingGroupId() { | |||
@@ -234,13 +255,13 @@ public final class EscherDggRecord extends EscherRecord { | |||
} | |||
} | |||
/** | |||
* Add a new cluster | |||
* | |||
* @param dgId id of the drawing group (stored in the record options) | |||
* @param numShapedUsed initial value of the numShapedUsed field | |||
* | |||
* | |||
* @return the new {@link FileIdCluster} | |||
*/ | |||
public FileIdCluster addCluster(int dgId, int numShapedUsed) { | |||
@@ -254,35 +275,29 @@ public final class EscherDggRecord extends EscherRecord { | |||
* @param numShapedUsed initial value of the numShapedUsed field | |||
* @param sort if true then sort clusters by drawing group id.( | |||
* In Excel the clusters are sorted but in PPT they are not) | |||
* | |||
* | |||
* @return the new {@link FileIdCluster} | |||
*/ | |||
public FileIdCluster addCluster( int dgId, int numShapedUsed, boolean sort ) { | |||
FileIdCluster ficNew = new FileIdCluster(dgId, numShapedUsed); | |||
field_5_fileIdClusters.add(ficNew); | |||
maxDgId = Math.min(maxDgId, dgId); | |||
if (sort) { | |||
sortCluster(); | |||
} | |||
return ficNew; | |||
} | |||
private void sortCluster() { | |||
field_5_fileIdClusters.sort(new Comparator<FileIdCluster>() { | |||
@Override | |||
public int compare(FileIdCluster f1, FileIdCluster f2) { | |||
int dgDif = f1.getDrawingGroupId() - f2.getDrawingGroupId(); | |||
int cntDif = f2.getNumShapeIdsUsed() - f1.getNumShapeIdsUsed(); | |||
return (dgDif != 0) ? dgDif : cntDif; | |||
} | |||
}); | |||
field_5_fileIdClusters.sort(FileIdCluster::compareFileIdCluster); | |||
} | |||
/** | |||
* Finds the next available (1 based) drawing group id | |||
* | |||
* | |||
* @return the next available drawing group id | |||
*/ | |||
public short findNewDrawingGroupId() { | |||
@@ -293,7 +308,7 @@ public final class EscherDggRecord extends EscherRecord { | |||
} | |||
return (short)bs.nextClearBit(0); | |||
} | |||
/** | |||
* Allocates new shape id for the drawing group | |||
* | |||
@@ -306,7 +321,7 @@ public final class EscherDggRecord extends EscherRecord { | |||
public int allocateShapeId(EscherDgRecord dg, boolean sort) { | |||
final short drawingGroupId = dg.getDrawingGroupId(); | |||
field_3_numShapesSaved++; | |||
// check for an existing cluster, which has space available | |||
// see 2.2.46 OfficeArtIDCL (cspidCur) for the 1024 limitation | |||
// multiple clusters can belong to the same drawing group | |||
@@ -325,16 +340,16 @@ public final class EscherDggRecord extends EscherRecord { | |||
ficAdd = addCluster( drawingGroupId, 0, sort ); | |||
maxDgId = Math.max(maxDgId, drawingGroupId); | |||
} | |||
int shapeId = index*1024 + ficAdd.getNumShapeIdsUsed(); | |||
ficAdd.incrementUsedShapeId(); | |||
dg.setNumShapes( dg.getNumShapes() + 1 ); | |||
dg.setLastMSOSPID( shapeId ); | |||
field_1_shapeIdMax = Math.max(field_1_shapeIdMax, shapeId + 1); | |||
return shapeId; | |||
} | |||
} | |||
@Override | |||
public Enum getGenericRecordType() { | |||
@@ -352,4 +367,9 @@ public final class EscherDggRecord extends EscherRecord { | |||
"drawingsSaved", this::getDrawingsSaved | |||
); | |||
} | |||
@Override | |||
public EscherDggRecord copy() { | |||
return new EscherDggRecord(this); | |||
} | |||
} |
@@ -65,6 +65,26 @@ public final class EscherMetafileBlip extends EscherBlipRecord { | |||
private byte[] raw_pictureData; | |||
private byte[] remainingData; | |||
public EscherMetafileBlip() {} | |||
public EscherMetafileBlip(EscherMetafileBlip other) { | |||
super(other); | |||
System.arraycopy(other.field_1_UID, 0, field_1_UID, 0, field_1_UID.length); | |||
System.arraycopy(other.field_2_UID, 0, field_2_UID, 0, field_2_UID.length); | |||
field_2_cb = other.field_2_cb; | |||
field_3_rcBounds_x1 = other.field_3_rcBounds_x1; | |||
field_3_rcBounds_y1 = other.field_3_rcBounds_y1; | |||
field_3_rcBounds_x2 = other.field_3_rcBounds_x2; | |||
field_3_rcBounds_y2 = other.field_3_rcBounds_y2; | |||
field_4_ptSize_h = other.field_4_ptSize_h; | |||
field_4_ptSize_w = other.field_4_ptSize_w; | |||
field_5_cbSave = other.field_5_cbSave; | |||
field_6_fCompression = other.field_6_fCompression; | |||
field_7_fFilter = other.field_7_fFilter; | |||
raw_pictureData = (other.raw_pictureData == null) ? null : other.raw_pictureData.clone(); | |||
remainingData = (other.remainingData == null) ? null : other.remainingData.clone(); | |||
} | |||
@Override | |||
public int fillFields(byte[] data, int offset, EscherRecordFactory recordFactory) { | |||
int bytesAfterHeader = readHeader( data, offset ); | |||
@@ -263,7 +283,7 @@ public final class EscherMetafileBlip extends EscherBlipRecord { | |||
} | |||
/** | |||
* Gets the dimensions of the metafile | |||
* Gets the dimensions of the metafile | |||
* | |||
* @return the dimensions of the metafile | |||
*/ | |||
@@ -283,7 +303,7 @@ public final class EscherMetafileBlip extends EscherBlipRecord { | |||
/** | |||
* Gets the compressed size of the metafile (in bytes) | |||
* | |||
* | |||
* @return the compressed size | |||
*/ | |||
public int getCompressedSize() { | |||
@@ -325,7 +345,7 @@ public final class EscherMetafileBlip extends EscherBlipRecord { | |||
public byte getFilter() { | |||
return field_7_fFilter; | |||
} | |||
/** | |||
* Sets the filter byte - this is usually 0xFE | |||
* | |||
@@ -335,8 +355,8 @@ public final class EscherMetafileBlip extends EscherBlipRecord { | |||
field_7_fFilter = filter; | |||
} | |||
/** | |||
* Returns any remaining bytes | |||
* | |||
@@ -345,7 +365,7 @@ public final class EscherMetafileBlip extends EscherBlipRecord { | |||
public byte[] getRemainingData() { | |||
return remainingData; | |||
} | |||
/** | |||
* Return the blip signature | |||
* | |||
@@ -381,7 +401,7 @@ public final class EscherMetafileBlip extends EscherBlipRecord { | |||
} catch (IOException e) { | |||
throw new RuntimeException("Can't compress metafile picture data", e); | |||
} | |||
setCompressedSize(raw_pictureData.length); | |||
setCompressed(true); | |||
} | |||
@@ -398,4 +418,9 @@ public final class EscherMetafileBlip extends EscherBlipRecord { | |||
m.put("filter", this::getFilter); | |||
return Collections.unmodifiableMap(m); | |||
} | |||
@Override | |||
public EscherMetafileBlip copy() { | |||
return new EscherMetafileBlip(this); | |||
} | |||
} |
@@ -24,11 +24,17 @@ import org.apache.poi.util.Internal; | |||
* or complex. Simple types are fixed length. Complex properties are variable | |||
* length. | |||
*/ | |||
public class EscherOptRecord extends AbstractEscherOptRecord | |||
{ | |||
public class EscherOptRecord extends AbstractEscherOptRecord { | |||
public static final short RECORD_ID = EscherRecordTypes.OPT.typeID; | |||
public static final String RECORD_DESCRIPTION = EscherRecordTypes.OPT.description; | |||
public EscherOptRecord() {} | |||
public EscherOptRecord(EscherOptRecord other) { | |||
super(other); | |||
} | |||
@Override | |||
public short getInstance() | |||
{ | |||
@@ -76,4 +82,9 @@ public class EscherOptRecord extends AbstractEscherOptRecord | |||
public Enum getGenericRecordType() { | |||
return EscherRecordTypes.OPT; | |||
} | |||
@Override | |||
public EscherOptRecord copy() { | |||
return new EscherOptRecord(this); | |||
} | |||
} |
@@ -58,6 +58,24 @@ public final class EscherPictBlip extends EscherBlipRecord { | |||
private byte[] raw_pictureData; | |||
public EscherPictBlip() {} | |||
public EscherPictBlip(EscherPictBlip other) { | |||
super(other); | |||
System.arraycopy(other.field_1_UID, 0, field_1_UID, 0, field_1_UID.length); | |||
field_2_cb = other.field_2_cb; | |||
field_3_rcBounds_x1 = other.field_3_rcBounds_x1; | |||
field_3_rcBounds_y1 = other.field_3_rcBounds_y1; | |||
field_3_rcBounds_x2 = other.field_3_rcBounds_x2; | |||
field_3_rcBounds_y2 = other.field_3_rcBounds_y2; | |||
field_4_ptSize_w = other.field_4_ptSize_w; | |||
field_4_ptSize_h = other.field_4_ptSize_h; | |||
field_5_cbSave = other.field_5_cbSave; | |||
field_6_fCompression = other.field_6_fCompression; | |||
field_7_fFilter = other.field_7_fFilter; | |||
raw_pictureData = (other.raw_pictureData == null) ? null : other.raw_pictureData.clone(); | |||
} | |||
@Override | |||
public int fillFields(byte[] data, int offset, EscherRecordFactory recordFactory) { | |||
int bytesAfterHeader = readHeader(data, offset); | |||
@@ -212,7 +230,7 @@ public final class EscherPictBlip extends EscherBlipRecord { | |||
} | |||
/** | |||
* Gets the dimensions of the metafile | |||
* Gets the dimensions of the metafile | |||
* | |||
* @return the dimensions of the metafile | |||
*/ | |||
@@ -232,7 +250,7 @@ public final class EscherPictBlip extends EscherBlipRecord { | |||
/** | |||
* Gets the compressed size of the metafile (in bytes) | |||
* | |||
* | |||
* @return the compressed size | |||
*/ | |||
public int getCompressedSize() { | |||
@@ -274,7 +292,7 @@ public final class EscherPictBlip extends EscherBlipRecord { | |||
public byte getFilter() { | |||
return field_7_fFilter; | |||
} | |||
/** | |||
* Sets the filter byte - this is usually 0xFE | |||
* | |||
@@ -296,4 +314,9 @@ public final class EscherPictBlip extends EscherBlipRecord { | |||
m.put("filter", this::getFilter); | |||
return Collections.unmodifiableMap(m); | |||
} | |||
@Override | |||
public EscherPictBlip copy() { | |||
return new EscherPictBlip(this); | |||
} | |||
} |
@@ -51,6 +51,11 @@ public abstract class EscherRecord implements Cloneable, GenericRecord { | |||
// fields uninitialised | |||
} | |||
protected EscherRecord(EscherRecord other) { | |||
_options = other._options; | |||
_recordId = other._recordId; | |||
} | |||
/** | |||
* Delegates to fillFields(byte[], int, EscherRecordFactory) | |||
* | |||
@@ -233,12 +238,11 @@ public abstract class EscherRecord implements Cloneable, GenericRecord { | |||
* Escher records may need to be clonable in the future. | |||
* | |||
* @return the cloned object | |||
* | |||
* @throws CloneNotSupportedException if the subclass hasn't implemented {@link Cloneable} | |||
*/ | |||
@Override | |||
public EscherRecord clone() throws CloneNotSupportedException { | |||
return (EscherRecord)super.clone(); | |||
@SuppressWarnings("squid:S2975") | |||
public final EscherRecord clone() { | |||
return copy(); | |||
} | |||
/** | |||
@@ -345,4 +349,6 @@ public abstract class EscherRecord implements Cloneable, GenericRecord { | |||
"recordSize", this::getRecordSize | |||
); | |||
} | |||
public abstract EscherRecord copy(); | |||
} |
@@ -78,6 +78,14 @@ public class EscherSpRecord extends EscherRecord { | |||
private int field_1_shapeId; | |||
private int field_2_flags; | |||
public EscherSpRecord() {} | |||
public EscherSpRecord(EscherSpRecord other) { | |||
super(other); | |||
field_1_shapeId = other.field_1_shapeId; | |||
field_2_flags = other.field_2_flags; | |||
} | |||
@Override | |||
public int fillFields(byte[] data, int offset, EscherRecordFactory recordFactory) { | |||
/*int bytesRemaining =*/ readHeader( data, offset ); | |||
@@ -169,7 +177,7 @@ public class EscherSpRecord extends EscherRecord { | |||
/** | |||
* Sets a number that identifies this shape. | |||
* | |||
* | |||
* @param field_1_shapeId the shape id | |||
*/ | |||
public void setShapeId( int field_1_shapeId ) | |||
@@ -179,7 +187,7 @@ public class EscherSpRecord extends EscherRecord { | |||
/** | |||
* The flags that apply to this shape. | |||
* | |||
* | |||
* @return the flags | |||
* | |||
* @see #FLAG_GROUP | |||
@@ -202,7 +210,7 @@ public class EscherSpRecord extends EscherRecord { | |||
/** | |||
* The flags that apply to this shape. | |||
* | |||
* | |||
* @param field_2_flags the flags | |||
* | |||
* @see #FLAG_GROUP | |||
@@ -226,7 +234,7 @@ public class EscherSpRecord extends EscherRecord { | |||
/** | |||
* Returns shape type. Must be one of MSOSPT values (see [MS-ODRAW] for | |||
* details). | |||
* | |||
* | |||
* @return shape type | |||
*/ | |||
public short getShapeType() | |||
@@ -237,7 +245,7 @@ public class EscherSpRecord extends EscherRecord { | |||
/** | |||
* Sets shape type. Must be one of MSOSPT values (see [MS-ODRAW] for | |||
* details). | |||
* | |||
* | |||
* @param value | |||
* new shape type | |||
*/ | |||
@@ -260,4 +268,9 @@ public class EscherSpRecord extends EscherRecord { | |||
public Enum getGenericRecordType() { | |||
return EscherRecordTypes.SP; | |||
} | |||
@Override | |||
public EscherSpRecord copy() { | |||
return new EscherSpRecord(this); | |||
} | |||
} |
@@ -36,6 +36,16 @@ public class EscherSpgrRecord extends EscherRecord { | |||
private int field_3_rectX2; | |||
private int field_4_rectY2; | |||
public EscherSpgrRecord() {} | |||
public EscherSpgrRecord(EscherSpgrRecord other) { | |||
super(other); | |||
field_1_rectX1 = other.field_1_rectX1; | |||
field_2_rectY1 = other.field_2_rectY1; | |||
field_3_rectX2 = other.field_3_rectX2; | |||
field_4_rectY2 = other.field_4_rectY2; | |||
} | |||
@Override | |||
public int fillFields(byte[] data, int offset, EscherRecordFactory recordFactory) { | |||
int bytesRemaining = readHeader( data, offset ); | |||
@@ -90,7 +100,7 @@ public class EscherSpgrRecord extends EscherRecord { | |||
/** | |||
* The starting top-left coordinate of child records. | |||
* | |||
* | |||
* @return the top-left x coordinate | |||
*/ | |||
public int getRectX1() | |||
@@ -100,7 +110,7 @@ public class EscherSpgrRecord extends EscherRecord { | |||
/** | |||
* The top-left coordinate of child records. | |||
* | |||
* | |||
* @param x1 the top-left x coordinate | |||
*/ | |||
public void setRectX1( int x1 ) | |||
@@ -110,7 +120,7 @@ public class EscherSpgrRecord extends EscherRecord { | |||
/** | |||
* The top-left coordinate of child records. | |||
* | |||
* | |||
* @return the top-left y coordinate | |||
*/ | |||
public int getRectY1() | |||
@@ -120,7 +130,7 @@ public class EscherSpgrRecord extends EscherRecord { | |||
/** | |||
* The top-left y coordinate of child records. | |||
* | |||
* | |||
* @param y1 the top-left y coordinate | |||
*/ | |||
public void setRectY1( int y1 ) | |||
@@ -130,7 +140,7 @@ public class EscherSpgrRecord extends EscherRecord { | |||
/** | |||
* The bottom-right x coordinate of child records. | |||
* | |||
* | |||
* @return the bottom-right x coordinate | |||
*/ | |||
public int getRectX2() | |||
@@ -140,7 +150,7 @@ public class EscherSpgrRecord extends EscherRecord { | |||
/** | |||
* The bottom-right x coordinate of child records. | |||
* | |||
* | |||
* @param x2 the bottom-right x coordinate | |||
*/ | |||
public void setRectX2( int x2 ) | |||
@@ -150,7 +160,7 @@ public class EscherSpgrRecord extends EscherRecord { | |||
/** | |||
* The bottom-right y coordinate of child records. | |||
* | |||
* | |||
* @return the bottom-right y coordinate | |||
*/ | |||
public int getRectY2() | |||
@@ -160,7 +170,7 @@ public class EscherSpgrRecord extends EscherRecord { | |||
/** | |||
* The bottom-right y coordinate of child records. | |||
* | |||
* | |||
* @param rectY2 the bottom-right y coordinate | |||
*/ | |||
public void setRectY2(int rectY2) { | |||
@@ -182,4 +192,9 @@ public class EscherSpgrRecord extends EscherRecord { | |||
public Enum getGenericRecordType() { | |||
return EscherRecordTypes.SPGR; | |||
} | |||
@Override | |||
public EscherSpgrRecord copy() { | |||
return new EscherSpgrRecord(this); | |||
} | |||
} |
@@ -36,6 +36,16 @@ public class EscherSplitMenuColorsRecord extends EscherRecord { | |||
private int field_3_color3; | |||
private int field_4_color4; | |||
public EscherSplitMenuColorsRecord() {} | |||
public EscherSplitMenuColorsRecord(EscherSplitMenuColorsRecord other) { | |||
super(other); | |||
field_1_color1 = other.field_1_color1; | |||
field_2_color2 = other.field_2_color2; | |||
field_3_color3 = other.field_3_color3; | |||
field_4_color4 = other.field_4_color4; | |||
} | |||
@Override | |||
public int fillFields(byte[] data, int offset, EscherRecordFactory recordFactory) { | |||
int bytesRemaining = readHeader( data, offset ); | |||
@@ -87,7 +97,7 @@ public class EscherSplitMenuColorsRecord extends EscherRecord { | |||
} | |||
/** | |||
* Gets the fill color | |||
* Gets the fill color | |||
* | |||
* @return the fill color | |||
*/ | |||
@@ -173,4 +183,9 @@ public class EscherSplitMenuColorsRecord extends EscherRecord { | |||
public Enum getGenericRecordType() { | |||
return EscherRecordTypes.SPLIT_MENU_COLORS; | |||
} | |||
@Override | |||
public EscherSplitMenuColorsRecord copy() { | |||
return new EscherSplitMenuColorsRecord(this); | |||
} | |||
} |
@@ -20,10 +20,15 @@ package org.apache.poi.ddf; | |||
* "The OfficeArtTertiaryFOPT record specifies a table of OfficeArtRGFOPTE properties, as defined in section 2.3.1." | |||
* -- [MS-ODRAW] -- v20110608; Office Drawing Binary File Format | |||
*/ | |||
public class EscherTertiaryOptRecord extends AbstractEscherOptRecord | |||
{ | |||
public class EscherTertiaryOptRecord extends AbstractEscherOptRecord { | |||
public static final short RECORD_ID = EscherRecordTypes.USER_DEFINED.typeID; | |||
public EscherTertiaryOptRecord() {} | |||
public EscherTertiaryOptRecord(EscherTertiaryOptRecord other) { | |||
super(other); | |||
} | |||
@Override | |||
public String getRecordName() { | |||
return EscherRecordTypes.USER_DEFINED.recordName; | |||
@@ -33,4 +38,9 @@ public class EscherTertiaryOptRecord extends AbstractEscherOptRecord | |||
public Enum getGenericRecordType() { | |||
return EscherRecordTypes.USER_DEFINED; | |||
} | |||
@Override | |||
public EscherTertiaryOptRecord copy() { | |||
return new EscherTertiaryOptRecord(this); | |||
} | |||
} |
@@ -43,8 +43,11 @@ public final class EscherTextboxRecord extends EscherRecord implements Cloneable | |||
/** The data for this record not including the the 8 byte header */ | |||
private byte[] thedata = NO_BYTES; | |||
public EscherTextboxRecord() | |||
{ | |||
public EscherTextboxRecord() {} | |||
public EscherTextboxRecord(EscherTextboxRecord other) { | |||
super(other); | |||
thedata = (other.thedata == null) ? NO_BYTES : other.thedata.clone(); | |||
} | |||
@Override | |||
@@ -83,7 +86,7 @@ public final class EscherTextboxRecord extends EscherRecord implements Cloneable | |||
* does not seem to put anything here, but with PowerPoint this will | |||
* contain the bytes that make up a TextHeaderAtom followed by a | |||
* TextBytesAtom/TextCharsAtom | |||
* | |||
* | |||
* @return the extra data | |||
*/ | |||
public byte[] getData() | |||
@@ -95,7 +98,7 @@ public final class EscherTextboxRecord extends EscherRecord implements Cloneable | |||
* Sets the extra data (in the parent application's format) to be | |||
* contained by the record. Used when the parent application changes | |||
* the contents. | |||
* | |||
* | |||
* @param b the buffer which contains the data | |||
* @param start the start position in the buffer | |||
* @param length the length of the block | |||
@@ -105,12 +108,12 @@ public final class EscherTextboxRecord extends EscherRecord implements Cloneable | |||
thedata = IOUtils.safelyAllocate(length, MAX_RECORD_LENGTH); | |||
System.arraycopy(b,start,thedata,0,length); | |||
} | |||
/** | |||
* Sets the extra data (in the parent application's format) to be | |||
* contained by the record. Used when the parent application changes | |||
* the contents. | |||
* | |||
* | |||
* @param b the data | |||
*/ | |||
public void setData(byte[] b) { | |||
@@ -123,15 +126,6 @@ public final class EscherTextboxRecord extends EscherRecord implements Cloneable | |||
return 8 + thedata.length; | |||
} | |||
@Override | |||
public EscherTextboxRecord clone() { | |||
EscherTextboxRecord etr = new EscherTextboxRecord(); | |||
etr.setOptions(this.getOptions()); | |||
etr.setRecordId(this.getRecordId()); | |||
etr.thedata = this.thedata.clone(); | |||
return etr; | |||
} | |||
@Override | |||
public String getRecordName() { | |||
return EscherRecordTypes.CLIENT_TEXTBOX.recordName; | |||
@@ -150,4 +144,9 @@ public final class EscherTextboxRecord extends EscherRecord implements Cloneable | |||
"extraData", this::getData | |||
); | |||
} | |||
@Override | |||
public EscherTextboxRecord copy() { | |||
return new EscherTextboxRecord(this); | |||
} | |||
} |
@@ -40,17 +40,20 @@ public final class UnknownEscherRecord extends EscherRecord implements Cloneable | |||
/** The data for this record not including the the 8 byte header */ | |||
private byte[] thedata = NO_BYTES; | |||
private List<EscherRecord> _childRecords; | |||
private final List<EscherRecord> _childRecords = new ArrayList<>(); | |||
public UnknownEscherRecord() { | |||
_childRecords = new ArrayList<>(); | |||
public UnknownEscherRecord() {} | |||
public UnknownEscherRecord(UnknownEscherRecord other) { | |||
super(other); | |||
other._childRecords.stream().map(EscherRecord::copy).forEach(_childRecords::add); | |||
} | |||
@Override | |||
public int fillFields(byte[] data, int offset, EscherRecordFactory recordFactory) { | |||
int bytesRemaining = readHeader( data, offset ); | |||
/* | |||
* Have a check between available bytes and bytesRemaining, | |||
* Have a check between available bytes and bytesRemaining, | |||
* take the available length if the bytesRemaining out of range. | |||
*/ | |||
int available = data.length - (offset + 8); | |||
@@ -77,7 +80,7 @@ public final class UnknownEscherRecord extends EscherRecord implements Cloneable | |||
if (bytesRemaining < 0) { | |||
bytesRemaining = 0; | |||
} | |||
thedata = IOUtils.safelyAllocate(bytesRemaining, MAX_RECORD_LENGTH); | |||
System.arraycopy( data, offset + 8, thedata, 0, bytesRemaining ); | |||
return bytesRemaining + 8; | |||
@@ -123,16 +126,11 @@ public final class UnknownEscherRecord extends EscherRecord implements Cloneable | |||
@Override | |||
public void setChildRecords(List<EscherRecord> childRecords) { | |||
_childRecords = childRecords; | |||
} | |||
@Override | |||
public UnknownEscherRecord clone() { | |||
UnknownEscherRecord uer = new UnknownEscherRecord(); | |||
uer.thedata = this.thedata.clone(); | |||
uer.setOptions(this.getOptions()); | |||
uer.setRecordId(this.getRecordId()); | |||
return uer; | |||
if (childRecords == _childRecords) { | |||
return; | |||
} | |||
_childRecords.clear(); | |||
_childRecords.addAll(childRecords); | |||
} | |||
@Override | |||
@@ -156,4 +154,9 @@ public final class UnknownEscherRecord extends EscherRecord implements Cloneable | |||
public Enum getGenericRecordType() { | |||
return EscherRecordTypes.UNKNOWN; | |||
} | |||
@Override | |||
public UnknownEscherRecord copy() { | |||
return new UnknownEscherRecord(this); | |||
} | |||
} |
@@ -42,6 +42,14 @@ public class EscherPlaceholder extends EscherRecord { | |||
public EscherPlaceholder() {} | |||
public EscherPlaceholder(EscherPlaceholder other) { | |||
super(other); | |||
position = other.position; | |||
placementId = other.placementId; | |||
size = other.size; | |||
unused = other.unused; | |||
} | |||
@Override | |||
public int fillFields(byte[] data, int offset, EscherRecordFactory recordFactory) { | |||
int bytesRemaining = readHeader( data, offset ); | |||
@@ -107,4 +115,9 @@ public class EscherPlaceholder extends EscherRecord { | |||
public Enum getGenericRecordType() { | |||
return RecordTypes.OEPlaceholderAtom; | |||
} | |||
@Override | |||
public EscherPlaceholder copy() { | |||
return new EscherPlaceholder(this); | |||
} | |||
} |
@@ -33,7 +33,7 @@ import org.apache.poi.util.LittleEndian; | |||
* An atom record that specifies whether a shape is a placeholder shape. | |||
* The number, position, and type of placeholder shapes are determined by | |||
* the slide layout as specified in the SlideAtom record. | |||
* | |||
* | |||
* @since POI 3.14-Beta2 | |||
*/ | |||
public class HSLFEscherClientDataRecord extends EscherClientDataRecord { | |||
@@ -42,19 +42,29 @@ public class HSLFEscherClientDataRecord extends EscherClientDataRecord { | |||
private static final int MAX_RECORD_LENGTH = 1_000_000; | |||
private final List<Record> _childRecords = new ArrayList<>(); | |||
public List<? extends Record> getHSLFChildRecords() { | |||
public HSLFEscherClientDataRecord() {} | |||
public HSLFEscherClientDataRecord(HSLFEscherClientDataRecord other) { | |||
super(other); | |||
// TODO: for now only reference others children, later copy them when Record.copy is available | |||
// other._childRecords.stream().map(Record::copy).forEach(_childRecords::add); | |||
other._childRecords.addAll(other._childRecords); | |||
} | |||
public List<? extends Record> getHSLFChildRecords() { | |||
return _childRecords; | |||
} | |||
public void removeChild(Class<? extends Record> childClass) { | |||
_childRecords.removeIf(childClass::isInstance); | |||
} | |||
public void addChild(Record childRecord) { | |||
_childRecords.add(childRecord); | |||
} | |||
@Override | |||
public int fillFields(byte[] data, int offset, EscherRecordFactory recordFactory) { | |||
int bytesRemaining = readHeader( data, offset ); | |||
@@ -67,12 +77,12 @@ public class HSLFEscherClientDataRecord extends EscherClientDataRecord { | |||
@Override | |||
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()); | |||
byte[] childBytes = getRemainingData(); | |||
LittleEndian.putInt(data, offset+4, childBytes.length); | |||
System.arraycopy(childBytes, 0, data, offset+8, childBytes.length); | |||
int recordSize = 8+childBytes.length; | |||
@@ -84,7 +94,7 @@ public class HSLFEscherClientDataRecord extends EscherClientDataRecord { | |||
public int getRecordSize() { | |||
return 8 + getRemainingData().length; | |||
} | |||
@Override | |||
public byte[] getRemainingData() { | |||
ByteArrayOutputStream bos = new ByteArrayOutputStream(); | |||
@@ -111,10 +121,13 @@ public class HSLFEscherClientDataRecord extends EscherClientDataRecord { | |||
offset += 8 + rlen; | |||
} | |||
} | |||
public String getRecordName() { | |||
return "HSLFClientData"; | |||
} | |||
@Override | |||
public HSLFEscherClientDataRecord copy() { | |||
return new HSLFEscherClientDataRecord(this); | |||
} | |||
} |
@@ -170,6 +170,8 @@ public final class TestEscherContainerRecord { | |||
public String getRecordName() { return ""; } | |||
@Override | |||
public Enum getGenericRecordType() { return EscherRecordTypes.UNKNOWN; } | |||
@Override | |||
public DummyEscherRecord copy() { return null; } | |||
} | |||
@Test |
@@ -17,11 +17,14 @@ | |||
package org.apache.poi.ddf; | |||
import static org.junit.Assert.assertArrayEquals; | |||
import static org.junit.Assert.assertTrue; | |||
import java.io.InputStream; | |||
import java.io.PrintStream; | |||
import java.io.UnsupportedEncodingException; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import org.apache.poi.POIDataSamples; | |||
import org.apache.poi.hssf.HSSFTestDataSamples; | |||
@@ -33,8 +36,35 @@ import org.junit.BeforeClass; | |||
import org.junit.Test; | |||
public class TestEscherDump { | |||
static NullPrinterStream nullPS; | |||
private static NullPrinterStream nullPS; | |||
private static final String recordData = | |||
"H4sIAAAAAAAAAL2UaVCSWxjHX0SBChABLRXM1FxSEzXTzHK7dpVIcMmwxXCP9KaGTaWlGYLrtGmGmYEmYmqF2qIt4ppmjNG+" + | |||
"2dWulUtOUdq1NHjva8v90HT7eM+Z5znP/M9/zpk5v3mONgAoc5AANBDKeVDW0gQAjZkVCti3mKnpAExpB/m8AKTyEiTCNd2J" + | |||
"Z+O0o6W+srDCyH3DhzkgUAD76v86QNA4mKTMg4QfnUew/5qA29CZz6ALqGcSgNzOICB05gD1rhODJR2AZu3Ox3YOKAfVUPhH" + | |||
"ULtbpdVJ0ccdijw0pY1A56M3Jup7U15jp7X4PPTTecx92/MT9eZwtUICrLJvsB6z0fHG5qbw7mRpFRaOnPYJ6SqXd5AQMSKM" + | |||
"jceyMD4NsULkj1OwHncz5cO3pPvCXXPTMNdNa+kDfwku4q0RnFL8YGBI6N+oXHlgzCkGWGRdONJPK1PbusJrhBltylPBMm3e" + | |||
"G0kw6DGdLhoU3pmgJ6n1maC1fXrs0uUL6cWG/kGVm3MWHh3pALq4+PH55k7Uu3d+x85u9zxwIzfQuU+3TIG5SkOgrS1tCJb3" + | |||
"3nqHrxcx3XlJA6vZJ6Oi1ctaXppQyBQLbLLrPJaKKq+zIexFLrVdZM+r34pJJpNN1hSrWbM/lIyRmKpYRIi7CybTTUzBWt49" + | |||
"11HRM/VbCiZ6Gyt9TZmhGXPS75xYjpH366vhgLJu4ZoZfM+/4FvGaBZIE9aZ2SduMrUT4mJA4NpP8F2AhB+dT+D/jY/7DZ84" + | |||
"ULbaK4C4crJvZ4qej2+em2+Vni4mPluh2e5xyoGUWYRaoFnWubHcaX+L09Ya0ta4KrP13C2ozMyicr4ovY0fNhT2wfMF7ip8" + | |||
"/tD0u96myXcn92gtTnEuGfBJxY0lFG0mJxPWpknjNxmzWvzKj18rpjO4hhQXAtaRVSmJu+D8egI3RdQVXYxzRhs1+HE2iNvM" + | |||
"fVe2DsSjqJQbBdUajcaECC3/58MP97Q0Eo+GNTdKbhk1r7UJadrVj0rLplmAqU/BlGeXDObGLtl69vITp9tD25vVY9vUT17u" + | |||
"WTGW8idcxUDMMX2PHa8X6xzG0C5cGJcVth40m3ycwCpcfuP4OClu6IpysV/9QuvrdW/Yb3Qj6Ul7e3nybqemdkvLXsXG2N3v" + | |||
"qeVE0woXb06pLduuFWUv7NxY8jq1k63fcD5jvG/w/IE8eUNh0Pohz0WRx6tdOlf4XhlbF5pgfYYzn8w6cjYx/8rBXvtWNz8L" + | |||
"6uu+ig35t+dgOc4jOpLirmFPtjQdKHovGZ4Bff4LaIPLnx6cbnKFo8JHDoGpJ1+BwKGfgM6GhB+d+F/0acj3PiVEABoProzN" + | |||
"1dcsVo9TPoPIF+r9EQI0qtveV4WEI1YhFjfmLxDsyFJptHvx/0BD3bfKJY/XqlFTReyIko4tQSzFFRyGRbkyg7MyuCqTmsiA" + | |||
"mAgs3FGB0BOR7LzNuUYMC9QWaXyUTcxELLOFQvaRIQZ1HlgkJtE25Ohym/xzkqxqbFI1fWKsGgKq0m/q9kwkVDJAvdKM+7c8" + | |||
"wj8GVPdneU0GVaeLVO6Kd3T2lMQFRNeCRwUyx5LSIxI5RmIFNc2RnuSIfYOeOZ+0CwzE7BFTJO+5cVeUz2nDN7mMYUSYOZyv" + | |||
"SyyaRRydLKPYMmqFbS5K8RJ6vQNIGtiuI8AKCEgXsqV9Vz1tgvzovKiD2FPtpNgRlb0keoprdS+hnsP6ICwLBrE9dz26g2YP" + | |||
"DszibWNE7zW5xndwlsoqFRh87XTFw8BXiFdv0SDsGBnfNcOu/Qu7y7SLppfzLJq714byzYQ590BA+BN2xyDhR+fZX7CL/s5u" + | |||
"Q9f/8ccWX28U3BaGw9qTiSqDfOtHmXXZq8XiHXAYoz901c5V2lVulTXZEMqwnLq8+8ds95s0FFrdl73saRntr/UuUxFHY0WU" + | |||
"z5b333qXTe/NagSRrmqkqypoNG12Oz3nE5Yzyt7d05eY66Ci2oTR+rNS3K4OiClGK+07HWtFFLvAqv6sNkpFsLs4Wp8XfRp/" + | |||
"11oPk3uTQB0ftHg1C16KRTBl+AbCzVaYfx6VFlJ7GL7Jme8bVOku8FKZL0eGgMVk4qhEnpZogNrtFU5yEyswJ+LbHOKsOPCn" + | |||
"cT19LR+PfTgjN4CKCS5Es4LS+7nLt9hQ7ejwGQnEyxebOgJzlHjotWUACpoZsFkAgGqBeUDZAzB6h4N2MFCNhmIuFJMAgPsH" + | |||
"eJr+iZEHAAA="; | |||
@BeforeClass | |||
public static void init() throws UnsupportedEncodingException { | |||
nullPS = new NullPrinterStream(); | |||
@@ -43,32 +73,6 @@ public class TestEscherDump { | |||
// simple test to at least cover some parts of the class | |||
@Test | |||
public void testSimple() throws Exception { | |||
final String recordData = | |||
"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" + | |||
"eJr+iZEHAAA="; | |||
// Create a new instance of the escher dumper | |||
EscherDump dumper = new EscherDump(); | |||
@@ -82,17 +86,17 @@ public class TestEscherDump { | |||
} | |||
@Test | |||
public void testWithData() throws Exception { | |||
public void testWithData() { | |||
new EscherDump().dump(8, new byte[] {0, 0, 0, 0, 0, 0, 0, 0}, nullPS); | |||
} | |||
@Test | |||
public void testWithSamplefile() throws Exception { | |||
public void testWithSamplefile() throws Exception { | |||
//InputStream stream = HSSFTestDataSamples.openSampleFileStream(") | |||
byte[] data = POIDataSamples.getDDFInstance().readFile("Container.dat"); | |||
new EscherDump().dump(data.length, data, nullPS); | |||
//new EscherDump().dumpOld(data.length, new ByteArrayInputStream(data), System.out); | |||
data = new byte[2586114]; | |||
try (InputStream stream = HSSFTestDataSamples.openSampleFileStream("44593.xls")) { | |||
int bytes = IOUtils.readFully(stream, data); | |||
@@ -101,7 +105,21 @@ public class TestEscherDump { | |||
//new EscherDump().dumpOld(bytes, new ByteArrayInputStream(data), System.out); | |||
} | |||
} | |||
@Test | |||
public void testCopy() throws Exception { | |||
byte[] data1 = RawDataUtil.decompress(recordData); | |||
List<EscherRecord> records = new ArrayList<>(); | |||
EscherRecordFactory recordFactory = new DefaultEscherRecordFactory(); | |||
EscherRecord r = recordFactory.createRecord(data1, 0); | |||
r.fillFields(data1, recordFactory); | |||
EscherRecord r2 = r.copy(); | |||
byte[] data2 = r2.serialize(); | |||
assertArrayEquals(data1, data2); | |||
} | |||
/** | |||
* Implementation of an OutputStream which does nothing, used | |||
* to redirect stdout to avoid spamming the console with output | |||
@@ -110,6 +128,6 @@ public class TestEscherDump { | |||
@SuppressWarnings("resource") | |||
private NullPrinterStream() throws UnsupportedEncodingException { | |||
super(new NullOutputStream(),true,LocaleUtil.CHARSET_1252.name()); | |||
} | |||
} | |||
} | |||
} | |||
} |