| /* ==================================================================== |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| ==================================================================== */ |
| |
| package org.apache.poi.ddf; |
| |
| import org.apache.poi.util.HexDump; |
| import org.apache.poi.util.LittleEndian; |
| |
| /** |
| * The escher client anchor specifies which rows and cells the shape is bound to as well as |
| * the offsets within those cells. Each cell is 1024 units wide by 256 units long regardless |
| * of the actual size of the cell. The EscherClientAnchorRecord only applies to the top-most |
| * shapes. Shapes contained in groups are bound using the EscherChildAnchorRecords. |
| * |
| * @see EscherChildAnchorRecord |
| */ |
| public class EscherClientAnchorRecord |
| extends EscherRecord |
| { |
| public static final short RECORD_ID = (short) 0xF010; |
| public static final String RECORD_DESCRIPTION = "MsofbtClientAnchor"; |
| |
| /** |
| * bit[0] - fMove (1 bit): A bit that specifies whether the shape will be kept intact when the cells are moved. |
| * bit[1] - fSize (1 bit): A bit that specifies whether the shape will be kept intact when the cells are resized. If fMove is 1, the value MUST be 1. |
| * bit[2-4] - reserved, MUST be 0 and MUST be ignored |
| * bit[5-15]- Undefined and MUST be ignored. |
| * |
| * it can take values: 0, 2, 3 |
| */ |
| private short field_1_flag; |
| private short field_2_col1; |
| private short field_3_dx1; |
| private short field_4_row1; |
| private short field_5_dy1; |
| private short field_6_col2; |
| private short field_7_dx2; |
| private short field_8_row2; |
| private short field_9_dy2; |
| private byte[] remainingData = new byte[0]; |
| private boolean shortRecord = false; |
| |
| @Override |
| public int fillFields(byte[] data, int offset, EscherRecordFactory recordFactory) { |
| int bytesRemaining = readHeader( data, offset ); |
| int pos = offset + 8; |
| int size = 0; |
| |
| // Always find 4 two byte entries. Sometimes find 9 |
| /*if (bytesRemaining == 4) // Word format only 4 bytes |
| { |
| // Not sure exactly what the format is quite yet, likely a reference to a PLC |
| } |
| else */ |
| if (bytesRemaining != 4) // Word format only 4 bytes |
| { |
| field_1_flag = LittleEndian.getShort( data, pos + size ); size += 2; |
| field_2_col1 = LittleEndian.getShort( data, pos + size ); size += 2; |
| field_3_dx1 = LittleEndian.getShort( data, pos + size ); size += 2; |
| field_4_row1 = LittleEndian.getShort( data, pos + size ); size += 2; |
| if(bytesRemaining >= 18) { |
| field_5_dy1 = LittleEndian.getShort( data, pos + size ); size += 2; |
| field_6_col2 = LittleEndian.getShort( data, pos + size ); size += 2; |
| field_7_dx2 = LittleEndian.getShort( data, pos + size ); size += 2; |
| field_8_row2 = LittleEndian.getShort( data, pos + size ); size += 2; |
| field_9_dy2 = LittleEndian.getShort( data, pos + size ); size += 2; |
| shortRecord = false; |
| } else { |
| shortRecord = true; |
| } |
| } |
| bytesRemaining -= size; |
| remainingData = new byte[bytesRemaining]; |
| System.arraycopy( data, pos + size, remainingData, 0, bytesRemaining ); |
| return 8 + size + bytesRemaining; |
| } |
| |
| @Override |
| public int serialize( int offset, byte[] data, EscherSerializationListener listener ) |
| { |
| listener.beforeRecordSerialize( offset, getRecordId(), this ); |
| |
| if (remainingData == null) remainingData = new byte[0]; |
| LittleEndian.putShort( data, offset, getOptions() ); |
| LittleEndian.putShort( data, offset + 2, getRecordId() ); |
| int remainingBytes = remainingData.length + (shortRecord ? 8 : 18); |
| LittleEndian.putInt( data, offset + 4, remainingBytes ); |
| LittleEndian.putShort( data, offset + 8, field_1_flag ); |
| LittleEndian.putShort( data, offset + 10, field_2_col1 ); |
| LittleEndian.putShort( data, offset + 12, field_3_dx1 ); |
| LittleEndian.putShort( data, offset + 14, field_4_row1 ); |
| if(!shortRecord) { |
| LittleEndian.putShort( data, offset + 16, field_5_dy1 ); |
| LittleEndian.putShort( data, offset + 18, field_6_col2 ); |
| LittleEndian.putShort( data, offset + 20, field_7_dx2 ); |
| LittleEndian.putShort( data, offset + 22, field_8_row2 ); |
| LittleEndian.putShort( data, offset + 24, field_9_dy2 ); |
| } |
| System.arraycopy( remainingData, 0, data, offset + (shortRecord ? 16 : 26), remainingData.length ); |
| int pos = offset + 8 + (shortRecord ? 8 : 18) + remainingData.length; |
| |
| listener.afterRecordSerialize( pos, getRecordId(), pos - offset, this ); |
| return pos - offset; |
| } |
| |
| @Override |
| public int getRecordSize() |
| { |
| return 8 + (shortRecord ? 8 : 18) + (remainingData == null ? 0 : remainingData.length); |
| } |
| |
| @Override |
| public short getRecordId() { |
| return RECORD_ID; |
| } |
| |
| @Override |
| public String getRecordName() { |
| return "ClientAnchor"; |
| } |
| |
| /** |
| * Returns the string representation for this record. |
| * |
| * @return A string |
| */ |
| @Override |
| public String toString() |
| { |
| String nl = System.getProperty("line.separator"); |
| String extraData = HexDump.dump(this.remainingData, 0, 0); |
| return getClass().getName() + ":" + nl + |
| " RecordId: 0x" + HexDump.toHex(RECORD_ID) + nl + |
| " Version: 0x" + HexDump.toHex(getVersion()) + nl + |
| " Instance: 0x" + HexDump.toHex(getInstance()) + nl + |
| " Flag: " + field_1_flag + nl + |
| " Col1: " + field_2_col1 + nl + |
| " DX1: " + field_3_dx1 + nl + |
| " Row1: " + field_4_row1 + nl + |
| " DY1: " + field_5_dy1 + nl + |
| " Col2: " + field_6_col2 + nl + |
| " DX2: " + field_7_dx2 + nl + |
| " Row2: " + field_8_row2 + nl + |
| " DY2: " + field_9_dy2 + nl + |
| " Extra Data:" + nl + extraData; |
| |
| } |
| |
| @Override |
| public String toXml(String tab) { |
| String extraData = HexDump.dump(this.remainingData, 0, 0).trim(); |
| return tab + formatXmlRecordHeader(getClass().getSimpleName(), HexDump.toHex(getRecordId()), HexDump.toHex(getVersion()), HexDump.toHex(getInstance())) + |
| tab + "\t" + "<Flag>" + field_1_flag + "</Flag>\n" + |
| tab + "\t" + "<Col1>" + field_2_col1 + "</Col1>\n" + |
| tab + "\t" + "<DX1>" + field_3_dx1 + "</DX1>\n" + |
| tab + "\t" + "<Row1>" + field_4_row1 + "</Row1>\n" + |
| tab + "\t" + "<DY1>" + field_5_dy1 + "</DY1>\n" + |
| tab + "\t" + "<Col2>" + field_6_col2 + "</Col2>\n" + |
| tab + "\t" + "<DX2>" + field_7_dx2 + "</DX2>\n" + |
| tab + "\t" + "<Row2>" + field_8_row2 + "</Row2>\n" + |
| tab + "\t" + "<DY2>" + field_9_dy2 + "</DY2>\n" + |
| tab + "\t" + "<ExtraData>" + extraData + "</ExtraData>\n" + |
| tab + "</" + getClass().getSimpleName() + ">\n"; |
| } |
| |
| /** |
| * 0 = Move and size with Cells, 2 = Move but don't size with cells, 3 = Don't move or size with cells. |
| * |
| * @return the move/size flag |
| */ |
| public short getFlag() |
| { |
| return field_1_flag; |
| } |
| |
| /** |
| * 0 = Move and size with Cells, 2 = Move but don't size with cells, 3 = Don't move or size with cells. |
| * |
| * @param field_1_flag the move/size flag |
| */ |
| public void setFlag( short field_1_flag ) |
| { |
| this.field_1_flag = field_1_flag; |
| } |
| |
| /** |
| * The column number for the top-left position. 0 based. |
| * |
| * @return the column number of the top-left corner |
| */ |
| public short getCol1() |
| { |
| return field_2_col1; |
| } |
| |
| /** |
| * The column number for the top-left position. 0 based. |
| * |
| * @param field_2_col1 the column number of the top-left corner |
| */ |
| public void setCol1( short field_2_col1 ) |
| { |
| this.field_2_col1 = field_2_col1; |
| } |
| |
| /** |
| * The x offset within the top-left cell. Range is from 0 to 1023. |
| * |
| * @return the x offset of the top-left corner |
| */ |
| public short getDx1() |
| { |
| return field_3_dx1; |
| } |
| |
| /** |
| * The x offset within the top-left cell. Range is from 0 to 1023. |
| * |
| * @param field_3_dx1 the x offset of the top-left corner |
| */ |
| public void setDx1( short field_3_dx1 ) |
| { |
| this.field_3_dx1 = field_3_dx1; |
| } |
| |
| /** |
| * The row number for the top-left corner of the shape. |
| * |
| * @return the row number of the top-left corner |
| */ |
| public short getRow1() |
| { |
| return field_4_row1; |
| } |
| |
| /** |
| * The row number of the top-left corner of the shape. |
| * |
| * @param field_4_row1 the row number of the top-left corner |
| */ |
| public void setRow1( short field_4_row1 ) |
| { |
| this.field_4_row1 = field_4_row1; |
| } |
| |
| /** |
| * The y offset within the top-left corner of the current shape. |
| * |
| * @return the y offset of the top-left corner |
| */ |
| public short getDy1() |
| { |
| return field_5_dy1; |
| } |
| |
| /** |
| * The y offset within the top-left corner of the current shape. |
| * |
| * @param field_5_dy1 the y offset of the top-left corner |
| */ |
| public void setDy1( short field_5_dy1 ) |
| { |
| shortRecord = false; |
| this.field_5_dy1 = field_5_dy1; |
| } |
| |
| /** |
| * The column of the bottom right corner of this shape. |
| * |
| * @return the column of the bottom right corner |
| */ |
| public short getCol2() |
| { |
| return field_6_col2; |
| } |
| |
| /** |
| * The column of the bottom right corner of this shape. |
| * |
| * @param field_6_col2 the column of the bottom right corner |
| */ |
| public void setCol2( short field_6_col2 ) |
| { |
| shortRecord = false; |
| this.field_6_col2 = field_6_col2; |
| } |
| |
| /** |
| * The x offset withing the cell for the bottom-right corner of this shape. |
| * |
| * @return the x offset of the bottom-right corner |
| */ |
| public short getDx2() |
| { |
| return field_7_dx2; |
| } |
| |
| /** |
| * The x offset withing the cell for the bottom-right corner of this shape. |
| * |
| * @param field_7_dx2 the x offset of the bottom-right corner |
| */ |
| public void setDx2( short field_7_dx2 ) |
| { |
| shortRecord = false; |
| this.field_7_dx2 = field_7_dx2; |
| } |
| |
| /** |
| * The row number for the bottom-right corner of the current shape. |
| * |
| * @return the row number for the bottom-right corner |
| */ |
| public short getRow2() |
| { |
| return field_8_row2; |
| } |
| |
| /** |
| * The row number for the bottom-right corner of the current shape. |
| * |
| * @param field_8_row2 the row number for the bottom-right corner |
| */ |
| public void setRow2( short field_8_row2 ) |
| { |
| shortRecord = false; |
| this.field_8_row2 = field_8_row2; |
| } |
| |
| /** |
| * The y offset withing the cell for the bottom-right corner of this shape. |
| * |
| * @return the y offset of the bottom-right corner |
| */ |
| public short getDy2() |
| { |
| return field_9_dy2; |
| } |
| |
| /** |
| * The y offset withing the cell for the bottom-right corner of this shape. |
| * |
| * @param field_9_dy2 the y offset of the bottom-right corner |
| */ |
| public void setDy2( short field_9_dy2 ) |
| { |
| shortRecord = false; |
| this.field_9_dy2 = field_9_dy2; |
| } |
| |
| /** |
| * Any remaining data in the record |
| * |
| * @return the remaining bytes |
| */ |
| public byte[] getRemainingData() |
| { |
| return remainingData; |
| } |
| |
| /** |
| * Any remaining data in the record |
| * |
| * @param remainingData the remaining bytes |
| */ |
| public void setRemainingData( byte[] remainingData ) { |
| if (remainingData == null) { |
| this.remainingData = new byte[0]; |
| } else { |
| this.remainingData = remainingData.clone(); |
| } |
| } |
| } |