* 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 )
/**
* The list of properties stored by this record.
- *
+ *
* @return the list of properties
*/
public List<EscherProperty> getEscherProperties()
/**
* The list of properties stored by this record.
- *
+ *
* @param index the ordinal index of the property
* @return the escher property
*/
return properties.get( index );
}
-
+
private int getPropertiesSize()
{
int totalSize = 0;
"properties", this::getEscherProperties
);
}
+
+ @Override
+ public abstract AbstractEscherOptRecord copy();
}
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 );
/**
* 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() {
/**
* Set the expected win32 blip type
- *
+ *
* @param blipTypeWin32 win32 blip type
*/
public void setBlipTypeWin32(byte blipTypeWin32) {
/**
* 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() {
/**
* Set the expected MacOS blip type
- *
+ *
* @param blipTypeMacOS MacOS blip type
*/
public void setBlipTypeMacOS(byte blipTypeMacOS) {
/**
* 16 byte MD4 checksum.
- *
+ *
* @return 16 byte MD4 checksum
*/
public byte[] getUid() {
/**
* 16 byte MD4 checksum.
- *
+ *
* @param uid 16 byte MD4 checksum
*/
public void setUid(byte[] uid) {
/**
* unused
- *
+ *
* @return an unknown tag
*/
public short getTag() {
/**
* unused
- *
+ *
* @param tag unknown tag
*/
public void setTag(short tag) {
/**
* Blip size in stream.
- *
+ *
* @return the blip size
*/
public int getSize() {
/**
* Blip size in stream.
- *
+ *
* @param size blip size
*/
public void setSize(int size) {
/**
* The reference count of this blip.
- *
+ *
* @return the reference count
*/
public int getRef() {
/**
* The reference count of this blip.
- *
+ *
* @param ref the reference count
*/
public void setRef(int ref) {
/**
* File offset in the delay stream.
- *
+ *
* @return the file offset
*/
public int getOffset() {
/**
* File offset in the delay stream.
- *
+ *
* @param offset the file offset
*/
public void setOffset(int offset) {
/**
* Defines the way this blip is used.
- *
+ *
* @return the blip usage
*/
public byte getUsage() {
/**
* Defines the way this blip is used.
- *
+ *
* @param usage the blip usae
*/
public void setUsage(byte usage) {
/**
* The length in characters of the blip name.
- *
+ *
* @return the blip name length
*/
public byte getName() {
/**
* The length in characters of the blip name.
- *
+ *
* @param name the blip name length
*/
public void setName(byte name) {
/**
* Any remaining data in this record.
- *
+ *
* @return the remaining bytes
*/
public byte[] getRemainingData() {
/**
* Any remaining data in this record.
- *
+ *
* @param remainingData the remaining bytes
*/
public void setRemainingData(byte[] remainingData) {
public Enum getGenericRecordType() {
return EscherRecordTypes.BSE;
}
+
+ @Override
+ public EscherBSERecord copy() {
+ return new EscherBSERecord(this);
+ }
}
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 );
);
}
+ @Override
+ public EscherBitmapBlip copy() {
+ return new EscherBitmapBlip(this);
+ }
}
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 );
EscherRecordTypes t = EscherRecordTypes.forTypeID(getRecordId());
return (t != EscherRecordTypes.UNKNOWN) ? t : EscherRecordTypes.BLIP_START;
}
+
+
+ @Override
+ public EscherBlipRecord copy() {
+ return new EscherBlipRecord(this);
+ }
}
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 );
default:
throw new RuntimeException("Invalid EscherChildAnchorRecord - neither 8 nor 16 bytes.");
}
-
+
return 8 + size;
}
/**
* Retrieves offset within the parent coordinate space for the top left point.
- *
+ *
* @return the x offset of the top left point
*/
public int getDx1()
/**
* 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 )
/**
* Gets offset within the parent coordinate space for the top left point.
- *
+ *
* @return the y offset of the top left point
*/
public int getDy1()
/**
* 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 )
{
/**
* Retrieves offset within the parent coordinate space for the bottom right point.
- *
+ *
* @return the x offset of the bottom right point
*/
public int getDx2()
/**
* 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 )
/**
* 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()
/**
* 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 )
public Enum getGenericRecordType() {
return EscherRecordTypes.CHILD_ANCHOR;
}
+
+ @Override
+ public EscherChildAnchorRecord copy() {
+ return new EscherChildAnchorRecord(this);
+ }
}
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 );
public Enum getGenericRecordType() {
return EscherRecordTypes.CLIENT_ANCHOR;
}
+
+ @Override
+ public EscherClientAnchorRecord copy() {
+ return new EscherClientAnchorRecord(this);
+ }
}
* 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;
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 );
/**
* Any data recording this record.
- *
+ *
* @return the remaining bytes
*/
public byte[] getRemainingData()
/**
* Any data recording this record.
- *
+ *
* @param remainingData the remaining bytes
*/
public void setRemainingData( byte[] remainingData ) {
public Enum getGenericRecordType() {
return EscherRecordTypes.CLIENT_DATA;
}
+
+ @Override
+ public EscherClientDataRecord copy() {
+ return new EscherClientDataRecord(this);
+ }
}
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);
public Enum getGenericRecordType() {
return EscherRecordTypes.forTypeID(getRecordId());
}
+
+ @Override
+ public EscherContainerRecord copy() {
+ return new EscherContainerRecord(this);
+ }
}
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 );
/**
* The number of shapes in this drawing group.
- *
+ *
* @return the number of shapes
*/
public int getNumShapes()
/**
* The number of shapes in this drawing group.
- *
+ *
* @param field_1_numShapes the number of shapes
*/
public void setNumShapes( int field_1_numShapes )
/**
* The last shape id used in this drawing group.
- *
+ *
* @return the last shape id
*/
public int getLastMSOSPID()
/**
* The last shape id used in this drawing group.
- *
+ *
* @param field_2_lastMSOSPID the last shape id
*/
public void setLastMSOSPID( int field_2_lastMSOSPID )
public Enum getGenericRecordType() {
return EscherRecordTypes.DG;
}
+
+ @Override
+ public EscherDgRecord copy() {
+ return new EscherDgRecord(this);
+ }
}
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;
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;
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(
}
}
+ 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 );
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 );
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;
/**
* The maximum is actually the next available shape id.
- *
+ *
* @param shapeIdMax the next available shape id
*/
public void setShapeIdMax(int shapeIdMax) {
/**
* Number of id clusters + 1
- *
+ *
* @return the number of id clusters + 1
*/
public int getNumIdClusters() {
/**
* Sets the number of shapes saved
- *
+ *
* @param numShapesSaved the number of shapes saved
*/
public void setNumShapesSaved(int numShapesSaved) {
/**
* Gets the maximum drawing group ID
- *
+ *
* @return The maximum drawing group ID
*/
public int getMaxDrawingGroupId() {
}
}
-
+
/**
* 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) {
* @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() {
}
return (short)bs.nextClearBit(0);
}
-
+
/**
* Allocates new shape id for the drawing group
*
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
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() {
"drawingsSaved", this::getDrawingsSaved
);
}
+
+ @Override
+ public EscherDggRecord copy() {
+ return new EscherDggRecord(this);
+ }
}
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 );
}
/**
- * Gets the dimensions of the metafile
+ * Gets the dimensions of the metafile
*
* @return the dimensions of the metafile
*/
/**
* Gets the compressed size of the metafile (in bytes)
- *
+ *
* @return the compressed size
*/
public int getCompressedSize() {
public byte getFilter() {
return field_7_fFilter;
}
-
+
/**
* Sets the filter byte - this is usually 0xFE
*
field_7_fFilter = filter;
}
-
-
+
+
/**
* Returns any remaining bytes
*
public byte[] getRemainingData() {
return remainingData;
}
-
+
/**
* Return the blip signature
*
} catch (IOException e) {
throw new RuntimeException("Can't compress metafile picture data", e);
}
-
+
setCompressedSize(raw_pictureData.length);
setCompressed(true);
}
m.put("filter", this::getFilter);
return Collections.unmodifiableMap(m);
}
+
+ @Override
+ public EscherMetafileBlip copy() {
+ return new EscherMetafileBlip(this);
+ }
}
* 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()
{
public Enum getGenericRecordType() {
return EscherRecordTypes.OPT;
}
+
+ @Override
+ public EscherOptRecord copy() {
+ return new EscherOptRecord(this);
+ }
}
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);
}
/**
- * Gets the dimensions of the metafile
+ * Gets the dimensions of the metafile
*
* @return the dimensions of the metafile
*/
/**
* Gets the compressed size of the metafile (in bytes)
- *
+ *
* @return the compressed size
*/
public int getCompressedSize() {
public byte getFilter() {
return field_7_fFilter;
}
-
+
/**
* Sets the filter byte - this is usually 0xFE
*
m.put("filter", this::getFilter);
return Collections.unmodifiableMap(m);
}
+
+ @Override
+ public EscherPictBlip copy() {
+ return new EscherPictBlip(this);
+ }
}
// fields uninitialised
}
+ protected EscherRecord(EscherRecord other) {
+ _options = other._options;
+ _recordId = other._recordId;
+ }
+
/**
* Delegates to fillFields(byte[], int, EscherRecordFactory)
*
* 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();
}
/**
"recordSize", this::getRecordSize
);
}
+
+ public abstract EscherRecord copy();
}
\ No newline at end of file
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 );
/**
* Sets a number that identifies this shape.
- *
+ *
* @param field_1_shapeId the shape id
*/
public void setShapeId( int field_1_shapeId )
/**
* The flags that apply to this shape.
- *
+ *
* @return the flags
*
* @see #FLAG_GROUP
/**
* The flags that apply to this shape.
- *
+ *
* @param field_2_flags the flags
*
* @see #FLAG_GROUP
/**
* Returns shape type. Must be one of MSOSPT values (see [MS-ODRAW] for
* details).
- *
+ *
* @return shape type
*/
public short getShapeType()
/**
* Sets shape type. Must be one of MSOSPT values (see [MS-ODRAW] for
* details).
- *
+ *
* @param value
* new shape type
*/
public Enum getGenericRecordType() {
return EscherRecordTypes.SP;
}
+
+ @Override
+ public EscherSpRecord copy() {
+ return new EscherSpRecord(this);
+ }
}
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 );
/**
* The starting top-left coordinate of child records.
- *
+ *
* @return the top-left x coordinate
*/
public int getRectX1()
/**
* The top-left coordinate of child records.
- *
+ *
* @param x1 the top-left x coordinate
*/
public void setRectX1( int x1 )
/**
* The top-left coordinate of child records.
- *
+ *
* @return the top-left y coordinate
*/
public int getRectY1()
/**
* The top-left y coordinate of child records.
- *
+ *
* @param y1 the top-left y coordinate
*/
public void setRectY1( int y1 )
/**
* The bottom-right x coordinate of child records.
- *
+ *
* @return the bottom-right x coordinate
*/
public int getRectX2()
/**
* The bottom-right x coordinate of child records.
- *
+ *
* @param x2 the bottom-right x coordinate
*/
public void setRectX2( int x2 )
/**
* The bottom-right y coordinate of child records.
- *
+ *
* @return the bottom-right y coordinate
*/
public int getRectY2()
/**
* The bottom-right y coordinate of child records.
- *
+ *
* @param rectY2 the bottom-right y coordinate
*/
public void setRectY2(int rectY2) {
public Enum getGenericRecordType() {
return EscherRecordTypes.SPGR;
}
+
+ @Override
+ public EscherSpgrRecord copy() {
+ return new EscherSpgrRecord(this);
+ }
}
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 );
}
/**
- * Gets the fill color
+ * Gets the fill color
*
* @return the fill color
*/
public Enum getGenericRecordType() {
return EscherRecordTypes.SPLIT_MENU_COLORS;
}
+
+ @Override
+ public EscherSplitMenuColorsRecord copy() {
+ return new EscherSplitMenuColorsRecord(this);
+ }
}
* "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;
public Enum getGenericRecordType() {
return EscherRecordTypes.USER_DEFINED;
}
+
+ @Override
+ public EscherTertiaryOptRecord copy() {
+ return new EscherTertiaryOptRecord(this);
+ }
}
/** 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
* 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()
* 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
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) {
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;
"extraData", this::getData
);
}
+
+ @Override
+ public EscherTextboxRecord copy() {
+ return new EscherTextboxRecord(this);
+ }
}
/** 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);
if (bytesRemaining < 0) {
bytesRemaining = 0;
}
-
+
thedata = IOUtils.safelyAllocate(bytesRemaining, MAX_RECORD_LENGTH);
System.arraycopy( data, offset + 8, thedata, 0, bytesRemaining );
return bytesRemaining + 8;
@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
public Enum getGenericRecordType() {
return EscherRecordTypes.UNKNOWN;
}
+
+ @Override
+ public UnknownEscherRecord copy() {
+ return new UnknownEscherRecord(this);
+ }
}
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 );
public Enum getGenericRecordType() {
return RecordTypes.OEPlaceholderAtom;
}
+
+ @Override
+ public EscherPlaceholder copy() {
+ return new EscherPlaceholder(this);
+ }
}
* 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 {
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 );
@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;
public int getRecordSize() {
return 8 + getRemainingData().length;
}
-
+
@Override
public byte[] getRemainingData() {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
offset += 8 + rlen;
}
}
-
+
public String getRecordName() {
return "HSLFClientData";
}
-
+ @Override
+ public HSLFEscherClientDataRecord copy() {
+ return new HSLFEscherClientDataRecord(this);
+ }
}
public String getRecordName() { return ""; }
@Override
public Enum getGenericRecordType() { return EscherRecordTypes.UNKNOWN; }
+ @Override
+ public DummyEscherRecord copy() { return null; }
}
@Test
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;
import org.junit.Test;
public class TestEscherDump {
- static NullPrinterStream nullPS;
-
+ private static NullPrinterStream nullPS;
+
+ private static final String recordData =
+ "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" +
+ "eJr+iZEHAAA=";
+
+
@BeforeClass
public static void init() throws UnsupportedEncodingException {
nullPS = new NullPrinterStream();
// 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();
}
@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);
//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
@SuppressWarnings("resource")
private NullPrinterStream() throws UnsupportedEncodingException {
super(new NullOutputStream(),true,LocaleUtil.CHARSET_1252.name());
- }
- }
+ }
+ }
}