<!-- Don't forget to update status.xml too! -->
<release version="3.2-alpha1" date="2008-??-??">
+ <action dev="POI-DEVELOPERS" type="fix">45964 - support for link formulas in Text Objects</action>
<action dev="POI-DEVELOPERS" type="fix">43354 - support for evalating formulas with missing args</action>
<action dev="POI-DEVELOPERS" type="fix">45912 - fixed ArrayIndexOutOfBoundsException in EmbeddedObjectRefSubRecord</action>
<action dev="POI-DEVELOPERS" type="fix">45889 - fixed ArrayIndexOutOfBoundsException when constructing HSLF Table with a single row </action>
<!-- Don't forget to update changes.xml too! -->
<changes>
<release version="3.2-alpha1" date="2008-??-??">
+ <action dev="POI-DEVELOPERS" type="fix">45964 - support for link formulas in Text Objects</action>
<action dev="POI-DEVELOPERS" type="fix">43354 - support for evalating formulas with missing args</action>
<action dev="POI-DEVELOPERS" type="fix">45912 - fixed ArrayIndexOutOfBoundsException in EmbeddedObjectRefSubRecord</action>
<action dev="POI-DEVELOPERS" type="fix">45889 - fixed ArrayIndexOutOfBoundsException when constructing HSLF Table with a single row </action>
HSSFTextbox shape = hssfShape;
TextObjectRecord obj = new TextObjectRecord();
- obj.setHorizontalTextAlignment( hssfShape.getHorizontalAlignment() );
- obj.setVerticalTextAlignment( hssfShape.getVerticalAlignment());
- obj.setTextLocked( true );
- obj.setTextOrientation( TextObjectRecord.TEXT_ORIENTATION_NONE );
- int frLength = ( shape.getString().numFormattingRuns() + 1 ) * 8;
- obj.setFormattingRunLength( (short) frLength );
- obj.setTextLength( (short) shape.getString().length() );
- obj.setStr( shape.getString() );
- obj.setReserved7( 0 );
+ obj.setHorizontalTextAlignment(hssfShape.getHorizontalAlignment());
+ obj.setVerticalTextAlignment(hssfShape.getVerticalAlignment());
+ obj.setTextLocked(true);
+ obj.setTextOrientation(TextObjectRecord.TEXT_ORIENTATION_NONE);
+ obj.setStr(shape.getString());
return obj;
}
import org.apache.poi.util.LittleEndian;
/**
- * Title: Continue Record - Helper class used primarily for SST Records <P>
+ * Title: Continue Record(0x003C) - Helper class used primarily for SST Records <P>
* Description: handles overflow for prior record in the input
* stream; content is tailored to that prior record<P>
* @author Marc Johnson (mjohnson at apache dot org)
* @author Andrew C. Oliver (acoliver at apache dot org)
* @author Csaba Nagy (ncsaba at yahoo dot com)
- * @version 2.0-pre
*/
-
-public class ContinueRecord
- extends Record
-{
+public final class ContinueRecord extends Record {
public final static short sid = 0x003C;
- private byte[] field_1_data;
-
- /**
- * default constructor
- */
+ private byte[] _data;
- public ContinueRecord()
- {
+ public ContinueRecord(byte[] data) {
+ _data = data;
}
/**
* USE ONLY within "processContinue"
*/
-
public byte [] serialize()
{
- byte[] retval = new byte[ field_1_data.length + 4 ];
+ byte[] retval = new byte[ _data.length + 4 ];
serialize(0, retval);
return retval;
}
- public int serialize(int offset, byte [] data)
- {
-
- LittleEndian.putShort(data, offset, sid);
- LittleEndian.putShort(data, offset + 2, ( short ) field_1_data.length);
- System.arraycopy(field_1_data, 0, data, offset + 4, field_1_data.length);
- return field_1_data.length + 4;
- // throw new RecordFormatException(
- // "You're not supposed to serialize Continue records like this directly");
- }
-
- /*
- * @param data raw data
- */
-
- public void setData(byte [] data)
- {
- field_1_data = data;
+ public int serialize(int offset, byte[] data) {
+ return write(data, offset, null, _data);
}
/**
* get the data for continuation
* @return byte array containing all of the continued data
*/
-
public byte [] getData()
{
- return field_1_data;
+ return _data;
}
- /**
- * Debugging toString
- *
- * @return string representation
- */
-
public String toString()
{
StringBuffer buffer = new StringBuffer();
*
* @param in the RecordInputstream to read the record from
*/
-
public ContinueRecord(RecordInputStream in)
{
- field_1_data = in.readRemainder();
+ _data = in.readRemainder();
}
- /**
- * Clone this record.
- */
public Object clone() {
- ContinueRecord clone = new ContinueRecord();
- clone.setData(field_1_data);
- return clone;
+ return new ContinueRecord(_data);
}
+ /**
+ * Writes the full encoding of a Continue record without making an instance
+ */
+ public static int write(byte[] destBuf, int destOffset, Byte initialDataByte, byte[] srcData) {
+ return write(destBuf, destOffset, initialDataByte, srcData, 0, srcData.length);
+ }
+ /**
+ * @param initialDataByte (optional - often used for unicode flag).
+ * If supplied, this will be written before srcData
+ * @return the total number of bytes written
+ */
+ public static int write(byte[] destBuf, int destOffset, Byte initialDataByte, byte[] srcData, int srcOffset, int len) {
+ int totalLen = len + (initialDataByte == null ? 0 : 1);
+ LittleEndian.putUShort(destBuf, destOffset, sid);
+ LittleEndian.putUShort(destBuf, destOffset + 2, totalLen);
+ int pos = destOffset + 4;
+ if (initialDataByte != null) {
+ LittleEndian.putByte(destBuf, pos, initialDataByte.byteValue());
+ pos += 1;
+ }
+ System.arraycopy(srcData, srcOffset, destBuf, pos, len);
+ return 4 + totalLen;
+ }
}
Record lastRecord = null;
while (recStream.hasNextRecord()) {
recStream.nextRecord();
- if (recStream.getSid() != 0) {
- Record[] recs = createRecord(recStream); // handle MulRK records
+ if (recStream.getSid() == 0) {
+ // After EOF, Excel seems to pad block with zeros
+ continue;
+ }
+ Record[] recs = createRecord(recStream); // handle MulRK records
- if (recs.length > 1) {
- for (int k = 0; k < recs.length; k++) {
- records.add(recs[ k ]); // these will be number records
- }
- } else {
- Record record = recs[ 0 ];
+ if (recs.length > 1) {
+ for (int k = 0; k < recs.length; k++) {
+ records.add(recs[ k ]); // these will be number records
+ }
+ continue;
+ }
+ Record record = recs[ 0 ];
- if (record != null) {
- if (record.getSid() == DrawingGroupRecord.sid
- && lastRecord instanceof DrawingGroupRecord) {
- DrawingGroupRecord lastDGRecord = (DrawingGroupRecord) lastRecord;
- lastDGRecord.join((AbstractEscherHolderRecord) record);
- } else if (record.getSid() == ContinueRecord.sid &&
- ((lastRecord instanceof ObjRecord) || (lastRecord instanceof TextObjectRecord))) {
- // Drawing records have a very strange continue behaviour.
- //There can actually be OBJ records mixed between the continues.
- lastDrawingRecord.processContinueRecord( ((ContinueRecord)record).getData() );
- //we must remember the position of the continue record.
- //in the serialization procedure the original structure of records must be preserved
- records.add(record);
- } else if (record.getSid() == ContinueRecord.sid &&
- (lastRecord instanceof DrawingGroupRecord)) {
- ((DrawingGroupRecord)lastRecord).processContinueRecord(((ContinueRecord)record).getData());
- } else if (record.getSid() == ContinueRecord.sid &&
- (lastRecord instanceof StringRecord)) {
- ((StringRecord)lastRecord).processContinueRecord(((ContinueRecord)record).getData());
- } else if (record.getSid() == ContinueRecord.sid) {
- if (lastRecord instanceof UnknownRecord) {
- //Gracefully handle records that we dont know about,
- //that happen to be continued
- records.add(record);
- } else
- throw new RecordFormatException("Unhandled Continue Record");
- } else {
- lastRecord = record;
- if (record instanceof DrawingRecord) {
- lastDrawingRecord = (DrawingRecord) record;
- }
- records.add(record);
- }
- }
+ if (record == null) {
+ continue;
+ }
+ if (record.getSid() == DrawingGroupRecord.sid
+ && lastRecord instanceof DrawingGroupRecord) {
+ DrawingGroupRecord lastDGRecord = (DrawingGroupRecord) lastRecord;
+ lastDGRecord.join((AbstractEscherHolderRecord) record);
+ } else if (record.getSid() == ContinueRecord.sid) {
+ ContinueRecord contRec = (ContinueRecord)record;
+
+ if (lastRecord instanceof ObjRecord || lastRecord instanceof TextObjectRecord) {
+ // Drawing records have a very strange continue behaviour.
+ //There can actually be OBJ records mixed between the continues.
+ lastDrawingRecord.processContinueRecord(contRec.getData() );
+ //we must remember the position of the continue record.
+ //in the serialization procedure the original structure of records must be preserved
+ records.add(record);
+ } else if (lastRecord instanceof DrawingGroupRecord) {
+ ((DrawingGroupRecord)lastRecord).processContinueRecord(contRec.getData());
+ } else if (lastRecord instanceof StringRecord) {
+ ((StringRecord)lastRecord).processContinueRecord(contRec.getData());
+ } else if (lastRecord instanceof UnknownRecord) {
+ //Gracefully handle records that we don't know about,
+ //that happen to be continued
+ records.add(record);
+ } else {
+ throw new RecordFormatException("Unhandled Continue Record");
+ }
+ } else {
+ lastRecord = record;
+ if (record instanceof DrawingRecord) {
+ lastDrawingRecord = (DrawingRecord) record;
}
+ records.add(record);
}
}
return records;
+++ /dev/null
-
-/* ====================================================================
- 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.hssf.record;
-
-import org.apache.poi.util.BitField;
-import org.apache.poi.util.BitFieldFactory;
-import org.apache.poi.util.HexDump;
-import org.apache.poi.util.LittleEndian;
-
-/**
- * The TXO record is used to define the properties of a text box. It is followed
- * by two continue records unless there is no actual text. The first continue record contains
- * the text data and the next continue record contains the formatting runs.<p/>
- *
- * @author Glen Stampoultzis (glens at apache.org)
- */
-public class TextObjectBaseRecord extends Record {
- // TODO - don't instantiate superclass
- public final static short sid = 0x01B6;
-
- private static final BitField reserved1 = BitFieldFactory.getInstance(0x0001);
- private static final BitField HorizontalTextAlignment = BitFieldFactory.getInstance(0x000E);
- private static final BitField VerticalTextAlignment = BitFieldFactory.getInstance(0x0070);
- private static final BitField reserved2 = BitFieldFactory.getInstance(0x0180);
- private static final BitField textLocked = BitFieldFactory.getInstance(0x0200);
- private static final BitField reserved3 = BitFieldFactory.getInstance(0xFC00);
-
- private short field_1_options;
- public final static short HORIZONTAL_TEXT_ALIGNMENT_LEFT_ALIGNED = 1;
- public final static short HORIZONTAL_TEXT_ALIGNMENT_CENTERED = 2;
- public final static short HORIZONTAL_TEXT_ALIGNMENT_RIGHT_ALIGNED = 3;
- public final static short HORIZONTAL_TEXT_ALIGNMENT_JUSTIFIED = 4;
- public final static short VERTICAL_TEXT_ALIGNMENT_TOP = 1;
- public final static short VERTICAL_TEXT_ALIGNMENT_CENTER = 2;
- public final static short VERTICAL_TEXT_ALIGNMENT_BOTTOM = 3;
- public final static short VERTICAL_TEXT_ALIGNMENT_JUSTIFY = 4;
- private short field_2_textOrientation;
- public final static short TEXT_ORIENTATION_NONE = 0;
- public final static short TEXT_ORIENTATION_TOP_TO_BOTTOM = 1;
- public final static short TEXT_ORIENTATION_ROT_RIGHT = 2;
- public final static short TEXT_ORIENTATION_ROT_LEFT = 3;
- private short field_3_reserved4;
- private short field_4_reserved5;
- private short field_5_reserved6;
- private short field_6_textLength;
- private short field_7_formattingRunLength;
- private int field_8_reserved7;
-
-
- public TextObjectBaseRecord()
- {
-
- }
-
- public TextObjectBaseRecord(RecordInputStream in)
- {
- field_1_options = in.readShort();
- field_2_textOrientation = in.readShort();
- field_3_reserved4 = in.readShort();
- field_4_reserved5 = in.readShort();
- field_5_reserved6 = in.readShort();
- field_6_textLength = in.readShort();
- field_7_formattingRunLength = in.readShort();
- field_8_reserved7 = in.readInt();
-
- }
-
- public String toString()
- {
- StringBuffer buffer = new StringBuffer();
-
- buffer.append("[TXO]\n");
- buffer.append(" .options = ")
- .append("0x").append(HexDump.toHex( getOptions ()))
- .append(" (").append( getOptions() ).append(" )");
- buffer.append(System.getProperty("line.separator"));
- buffer.append(" .reserved1 = ").append(isReserved1()).append('\n');
- buffer.append(" .HorizontalTextAlignment = ").append(getHorizontalTextAlignment()).append('\n');
- buffer.append(" .VerticalTextAlignment = ").append(getVerticalTextAlignment()).append('\n');
- buffer.append(" .reserved2 = ").append(getReserved2()).append('\n');
- buffer.append(" .textLocked = ").append(isTextLocked()).append('\n');
- buffer.append(" .reserved3 = ").append(getReserved3()).append('\n');
- buffer.append(" .textOrientation = ")
- .append("0x").append(HexDump.toHex( getTextOrientation ()))
- .append(" (").append( getTextOrientation() ).append(" )");
- buffer.append(System.getProperty("line.separator"));
- buffer.append(" .reserved4 = ")
- .append("0x").append(HexDump.toHex( getReserved4 ()))
- .append(" (").append( getReserved4() ).append(" )");
- buffer.append(System.getProperty("line.separator"));
- buffer.append(" .reserved5 = ")
- .append("0x").append(HexDump.toHex( getReserved5 ()))
- .append(" (").append( getReserved5() ).append(" )");
- buffer.append(System.getProperty("line.separator"));
- buffer.append(" .reserved6 = ")
- .append("0x").append(HexDump.toHex( getReserved6 ()))
- .append(" (").append( getReserved6() ).append(" )");
- buffer.append(System.getProperty("line.separator"));
- buffer.append(" .textLength = ")
- .append("0x").append(HexDump.toHex( getTextLength ()))
- .append(" (").append( getTextLength() ).append(" )");
- buffer.append(System.getProperty("line.separator"));
- buffer.append(" .formattingRunLength = ")
- .append("0x").append(HexDump.toHex( getFormattingRunLength ()))
- .append(" (").append( getFormattingRunLength() ).append(" )");
- buffer.append(System.getProperty("line.separator"));
- buffer.append(" .reserved7 = ")
- .append("0x").append(HexDump.toHex( getReserved7 ()))
- .append(" (").append( getReserved7() ).append(" )");
- buffer.append(System.getProperty("line.separator"));
-
- buffer.append("[/TXO]\n");
- return buffer.toString();
- }
-
- public int serialize(int offset, byte[] data)
- {
- int pos = 0;
-
- LittleEndian.putShort(data, 0 + offset, sid);
- LittleEndian.putShort(data, 2 + offset, (short)(getRecordSize() - 4));
-
- LittleEndian.putShort(data, 4 + offset + pos, field_1_options);
- LittleEndian.putShort(data, 6 + offset + pos, field_2_textOrientation);
- LittleEndian.putShort(data, 8 + offset + pos, field_3_reserved4);
- LittleEndian.putShort(data, 10 + offset + pos, field_4_reserved5);
- LittleEndian.putShort(data, 12 + offset + pos, field_5_reserved6);
- LittleEndian.putShort(data, 14 + offset + pos, field_6_textLength);
- LittleEndian.putShort(data, 16 + offset + pos, field_7_formattingRunLength);
- LittleEndian.putInt(data, 18 + offset + pos, field_8_reserved7);
-
- return getRecordSize();
- }
-
- public int getRecordSize()
- {
- return 4 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 4;
- }
-
- public short getSid()
- {
- return sid;
- }
-
- public Object clone() {
- TextObjectBaseRecord rec = new TextObjectBaseRecord();
-
- rec.field_1_options = field_1_options;
- rec.field_2_textOrientation = field_2_textOrientation;
- rec.field_3_reserved4 = field_3_reserved4;
- rec.field_4_reserved5 = field_4_reserved5;
- rec.field_5_reserved6 = field_5_reserved6;
- rec.field_6_textLength = field_6_textLength;
- rec.field_7_formattingRunLength = field_7_formattingRunLength;
- rec.field_8_reserved7 = field_8_reserved7;
- return rec;
- }
-
-
-
-
- /**
- * Get the options field for the TextObjectBase record.
- */
- public short getOptions()
- {
- return field_1_options;
- }
-
- /**
- * Set the options field for the TextObjectBase record.
- */
- public void setOptions(short field_1_options)
- {
- this.field_1_options = field_1_options;
- }
-
- /**
- * Get the text orientation field for the TextObjectBase record.
- *
- * @return One of
- * TEXT_ORIENTATION_NONE
- * TEXT_ORIENTATION_TOP_TO_BOTTOM
- * TEXT_ORIENTATION_ROT_RIGHT
- * TEXT_ORIENTATION_ROT_LEFT
- */
- public short getTextOrientation()
- {
- return field_2_textOrientation;
- }
-
- /**
- * Set the text orientation field for the TextObjectBase record.
- *
- * @param field_2_textOrientation
- * One of
- * TEXT_ORIENTATION_NONE
- * TEXT_ORIENTATION_TOP_TO_BOTTOM
- * TEXT_ORIENTATION_ROT_RIGHT
- * TEXT_ORIENTATION_ROT_LEFT
- */
- public void setTextOrientation(short field_2_textOrientation)
- {
- this.field_2_textOrientation = field_2_textOrientation;
- }
-
- /**
- * Get the reserved4 field for the TextObjectBase record.
- */
- public short getReserved4()
- {
- return field_3_reserved4;
- }
-
- /**
- * Set the reserved4 field for the TextObjectBase record.
- */
- public void setReserved4(short field_3_reserved4)
- {
- this.field_3_reserved4 = field_3_reserved4;
- }
-
- /**
- * Get the reserved5 field for the TextObjectBase record.
- */
- public short getReserved5()
- {
- return field_4_reserved5;
- }
-
- /**
- * Set the reserved5 field for the TextObjectBase record.
- */
- public void setReserved5(short field_4_reserved5)
- {
- this.field_4_reserved5 = field_4_reserved5;
- }
-
- /**
- * Get the reserved6 field for the TextObjectBase record.
- */
- public short getReserved6()
- {
- return field_5_reserved6;
- }
-
- /**
- * Set the reserved6 field for the TextObjectBase record.
- */
- public void setReserved6(short field_5_reserved6)
- {
- this.field_5_reserved6 = field_5_reserved6;
- }
-
- /**
- * Get the text length field for the TextObjectBase record.
- */
- public short getTextLength()
- {
- return field_6_textLength;
- }
-
- /**
- * Set the text length field for the TextObjectBase record.
- */
- public void setTextLength(short field_6_textLength)
- {
- this.field_6_textLength = field_6_textLength;
- }
-
- /**
- * Get the formatting run length field for the TextObjectBase record.
- */
- public short getFormattingRunLength()
- {
- return field_7_formattingRunLength;
- }
-
- /**
- * Set the formatting run length field for the TextObjectBase record.
- */
- public void setFormattingRunLength(short field_7_formattingRunLength)
- {
- this.field_7_formattingRunLength = field_7_formattingRunLength;
- }
-
- /**
- * Get the reserved7 field for the TextObjectBase record.
- */
- public int getReserved7()
- {
- return field_8_reserved7;
- }
-
- /**
- * Set the reserved7 field for the TextObjectBase record.
- */
- public void setReserved7(int field_8_reserved7)
- {
- this.field_8_reserved7 = field_8_reserved7;
- }
-
- /**
- * Sets the reserved1 field value.
- * reserved field
- */
- public void setReserved1(boolean value)
- {
- field_1_options = reserved1.setShortBoolean(field_1_options, value);
- }
-
- /**
- * reserved field
- * @return the reserved1 field value.
- */
- public boolean isReserved1()
- {
- return reserved1.isSet(field_1_options);
- }
-
- /**
- * Sets the Horizontal text alignment field value.
- *
- */
- public void setHorizontalTextAlignment(short value)
- {
- field_1_options = HorizontalTextAlignment.setShortValue(field_1_options, value);
- }
-
- /**
- *
- * @return the Horizontal text alignment field value.
- */
- public short getHorizontalTextAlignment()
- {
- return HorizontalTextAlignment.getShortValue(field_1_options);
- }
-
- /**
- * Sets the Vertical text alignment field value.
- *
- */
- public void setVerticalTextAlignment(short value)
- {
- field_1_options = VerticalTextAlignment.setShortValue(field_1_options, value);
- }
-
- /**
- *
- * @return the Vertical text alignment field value.
- */
- public short getVerticalTextAlignment()
- {
- return VerticalTextAlignment.getShortValue(field_1_options);
- }
-
- /**
- * Sets the reserved2 field value.
- *
- */
- public void setReserved2(short value)
- {
- field_1_options = reserved2.setShortValue(field_1_options, value);
- }
-
- /**
- *
- * @return the reserved2 field value.
- */
- public short getReserved2()
- {
- return reserved2.getShortValue(field_1_options);
- }
-
- /**
- * Sets the text locked field value.
- * Text has been locked
- */
- public void setTextLocked(boolean value)
- {
- field_1_options = textLocked.setShortBoolean(field_1_options, value);
- }
-
- /**
- * Text has been locked
- * @return the text locked field value.
- */
- public boolean isTextLocked()
- {
- return textLocked.isSet(field_1_options);
- }
-
- /**
- * Sets the reserved3 field value.
- *
- */
- public void setReserved3(short value)
- {
- field_1_options = reserved3.setShortValue(field_1_options, value);
- }
-
- /**
- *
- * @return the reserved3 field value.
- */
- public short getReserved3()
- {
- return reserved3.getShortValue(field_1_options);
- }
-}
import java.io.UnsupportedEncodingException;
+import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
+import org.apache.poi.util.BitField;
+import org.apache.poi.util.BitFieldFactory;
import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian;
-public class TextObjectRecord
- extends TextObjectBaseRecord
-{
- HSSFRichTextString str;
-
- public TextObjectRecord()
- {
- }
-
- public TextObjectRecord( RecordInputStream in )
- {
- super( in );
-
- if (getTextLength() > 0) {
- if (in.isContinueNext() && in.remaining() == 0) {
- //1st Continue
- in.nextRecord();
- processRawString(in);
- } else
- throw new RecordFormatException("Expected Continue record to hold string data for TextObjectRecord");
- }
- if (getFormattingRunLength() > 0) {
- if (in.isContinueNext() && in.remaining() == 0) {
- in.nextRecord();
- processFontRuns(in);
- } else throw new RecordFormatException("Expected Continue Record to hold font runs for TextObjectRecord");
- }
- if (str == null)
- str = new HSSFRichTextString("");
- }
-
-
- public int getRecordSize()
- {
- int continue1Size = 0;
- int continue2Size = 0;
- if (str.length() != 0)
- {
- int length = str.length() * 2;
- while(length > 0){
- int chunkSize = Math.min(RecordInputStream.MAX_RECORD_DATA_SIZE-2, length);
- length -= chunkSize;
-
- continue1Size += chunkSize;
- continue1Size += 1 + 4;
- }
-
- continue2Size = (str.numFormattingRuns() + 1) * 8 + 4;
- }
- return super.getRecordSize() + continue1Size + continue2Size;
- }
-
-
-
- public int serialize( int offset, byte[] data )
- {
- // Temporarily blank out str so that record size is calculated without the continue records.
- HSSFRichTextString temp = str;
- str = new HSSFRichTextString("");
- int bytesWritten1 = super.serialize( offset, data );
- str = temp;
-
- int pos = offset + bytesWritten1;
- if ( str.getString().equals( "" ) == false )
- {
- ContinueRecord c2 = createContinue2();
- int bytesWritten2 = 0;
-
- try
- {
- byte[] c1Data = str.getString().getBytes( "UTF-16LE" );
- int length = c1Data.length;
-
- int charsWritten = 0;
- int spos = pos;
- while(length > 0){
- int chunkSize = Math.min(RecordInputStream.MAX_RECORD_DATA_SIZE-2 , length);
- length -= chunkSize;
-
- //continue header
- LittleEndian.putShort(data, spos, ContinueRecord.sid);
- spos += LittleEndian.SHORT_SIZE;
- LittleEndian.putShort(data, spos, (short)(chunkSize+1));
- spos += LittleEndian.SHORT_SIZE;
-
- //The first byte specifies if the text is compressed unicode or unicode.
- //(regardless what was read, we always serialize double-byte unicode characters (UTF-16LE).
- data[spos] = 1;
- spos += LittleEndian.BYTE_SIZE;
-
- //copy characters data
- System.arraycopy(c1Data, charsWritten, data, spos, chunkSize);
- spos += chunkSize;
- charsWritten += chunkSize;
- }
-
- bytesWritten2 = (spos-pos);
- }
- catch ( UnsupportedEncodingException e )
- {
- throw new RuntimeException( e.getMessage(), e );
- }
-
- pos += bytesWritten2;
- int bytesWritten3 = c2.serialize( pos, data );
- pos += bytesWritten3;
-
- int size = bytesWritten1 + bytesWritten2 + bytesWritten3;
- if ( size != getRecordSize() )
- throw new RecordFormatException(size + " bytes written but getRecordSize() reports " + getRecordSize());
- return size;
- }
- if ( bytesWritten1 != getRecordSize() )
- throw new RecordFormatException(bytesWritten1 + " bytes written but getRecordSize() reports " + getRecordSize());
- return bytesWritten1;
- }
-
- private ContinueRecord createContinue2()
- {
- ContinueRecord c2 = new ContinueRecord();
- byte[] c2Data = new byte[str.numFormattingRuns() * 8 + 8];
- int pos = 0;
- for ( int i = 0; i < str.numFormattingRuns(); i++ )
- {
- LittleEndian.putShort( c2Data, pos, (short) str.getIndexOfFormattingRun( i ) );
- pos += 2;
- LittleEndian.putShort( c2Data, pos, str.getFontOfFormattingRun( i ) == str.NO_FONT ? 0 : str.getFontOfFormattingRun( i ) );
- pos += 2;
- pos += 4; // skip reserved
- }
- LittleEndian.putShort( c2Data, pos, (short) str.length() );
- pos += 2;
- LittleEndian.putShort( c2Data, pos, (short) 0 );
- pos += 2;
- pos += 4; // skip reserved
-
- c2.setData( c2Data );
-
- return c2;
- }
-
- private void processFontRuns( RecordInputStream in )
- {
- while (in.remaining() > 0)
- {
- short index = in.readShort();
- short iFont = in.readShort();
- in.readInt(); // skip reserved.
-
- str.applyFont( index, str.length(), iFont );
- }
- }
-
- private void processRawString( RecordInputStream in )
- {
- String s;
- byte compressByte = in.readByte();
- boolean isCompressed = compressByte == 0;
- if ( isCompressed )
- {
- s = in.readCompressedUnicode(getTextLength());
- }
- else
- {
- s = in.readUnicodeLEString(getTextLength());
- }
- str = new HSSFRichTextString( s );
- }
-
- public HSSFRichTextString getStr()
- {
- return str;
- }
-
- public void setStr( HSSFRichTextString str )
- {
- this.str = str;
- }
-
- public String toString()
- {
- StringBuffer buffer = new StringBuffer();
-
- buffer.append( "[TXO]\n" );
- buffer.append( " .options = " )
- .append( "0x" ).append( HexDump.toHex( getOptions() ) )
- .append( " (" ).append( getOptions() ).append( " )" );
- buffer.append( System.getProperty( "line.separator" ) );
- buffer.append( " .reserved1 = " ).append( isReserved1() ).append( '\n' );
- buffer.append( " .HorizontalTextAlignment = " ).append( getHorizontalTextAlignment() ).append( '\n' );
- buffer.append( " .VerticalTextAlignment = " ).append( getVerticalTextAlignment() ).append( '\n' );
- buffer.append( " .reserved2 = " ).append( getReserved2() ).append( '\n' );
- buffer.append( " .textLocked = " ).append( isTextLocked() ).append( '\n' );
- buffer.append( " .reserved3 = " ).append( getReserved3() ).append( '\n' );
- buffer.append( " .textOrientation = " )
- .append( "0x" ).append( HexDump.toHex( getTextOrientation() ) )
- .append( " (" ).append( getTextOrientation() ).append( " )" );
- buffer.append( System.getProperty( "line.separator" ) );
- buffer.append( " .reserved4 = " )
- .append( "0x" ).append( HexDump.toHex( getReserved4() ) )
- .append( " (" ).append( getReserved4() ).append( " )" );
- buffer.append( System.getProperty( "line.separator" ) );
- buffer.append( " .reserved5 = " )
- .append( "0x" ).append( HexDump.toHex( getReserved5() ) )
- .append( " (" ).append( getReserved5() ).append( " )" );
- buffer.append( System.getProperty( "line.separator" ) );
- buffer.append( " .reserved6 = " )
- .append( "0x" ).append( HexDump.toHex( getReserved6() ) )
- .append( " (" ).append( getReserved6() ).append( " )" );
- buffer.append( System.getProperty( "line.separator" ) );
- buffer.append( " .textLength = " )
- .append( "0x" ).append( HexDump.toHex( getTextLength() ) )
- .append( " (" ).append( getTextLength() ).append( " )" );
- buffer.append( System.getProperty( "line.separator" ) );
- buffer.append( " .reserved7 = " )
- .append( "0x" ).append( HexDump.toHex( getReserved7() ) )
- .append( " (" ).append( getReserved7() ).append( " )" );
- buffer.append( System.getProperty( "line.separator" ) );
-
- buffer.append( " .string = " ).append(str).append('\n');
-
- for (int i = 0; i < str.numFormattingRuns(); i++) {
- buffer.append( " .textrun = " ).append(str.getFontOfFormattingRun(i)).append('\n');
-
- }
- buffer.append( "[/TXO]\n" );
- return buffer.toString();
- }
-
- public Object clone() {
-
- TextObjectRecord rec = new TextObjectRecord();
- rec.str = str;
-
- rec.setOptions(getOptions());
- rec.setTextOrientation(getTextOrientation());
- rec.setReserved4(getReserved4());
- rec.setReserved5(getReserved5());
- rec.setReserved6(getReserved6());
- rec.setTextLength(getTextLength());
- rec.setFormattingRunLength(getFormattingRunLength());
- rec.setReserved7(getReserved7());
- return rec;
- }
-
+/**
+ * The TXO record (0x01B6) is used to define the properties of a text box. It is
+ * followed by two or more continue records unless there is no actual text. The
+ * first continue records contain the text data and the last continue record
+ * contains the formatting runs.<p/>
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public final class TextObjectRecord extends Record {
+ public final static short sid = 0x01B6;
+
+ private static final int FORMAT_RUN_ENCODED_SIZE = 8; // 2 shorts and 4 bytes reserved
+
+ private static final BitField HorizontalTextAlignment = BitFieldFactory.getInstance(0x000E);
+ private static final BitField VerticalTextAlignment = BitFieldFactory.getInstance(0x0070);
+ private static final BitField textLocked = BitFieldFactory.getInstance(0x0200);
+
+ public final static short HORIZONTAL_TEXT_ALIGNMENT_LEFT_ALIGNED = 1;
+ public final static short HORIZONTAL_TEXT_ALIGNMENT_CENTERED = 2;
+ public final static short HORIZONTAL_TEXT_ALIGNMENT_RIGHT_ALIGNED = 3;
+ public final static short HORIZONTAL_TEXT_ALIGNMENT_JUSTIFIED = 4;
+ public final static short VERTICAL_TEXT_ALIGNMENT_TOP = 1;
+ public final static short VERTICAL_TEXT_ALIGNMENT_CENTER = 2;
+ public final static short VERTICAL_TEXT_ALIGNMENT_BOTTOM = 3;
+ public final static short VERTICAL_TEXT_ALIGNMENT_JUSTIFY = 4;
+
+ public final static short TEXT_ORIENTATION_NONE = 0;
+ public final static short TEXT_ORIENTATION_TOP_TO_BOTTOM = 1;
+ public final static short TEXT_ORIENTATION_ROT_RIGHT = 2;
+ public final static short TEXT_ORIENTATION_ROT_LEFT = 3;
+
+ private int field_1_options;
+ private int field_2_textOrientation;
+ private int field_3_reserved4;
+ private int field_4_reserved5;
+ private int field_5_reserved6;
+ private int field_8_reserved7;
+
+ private HSSFRichTextString _text;
+
+ /*
+ * Note - the next three fields are very similar to those on
+ * EmbededObjectRefSubRecord(ftPictFmla 0x0009)
+ *
+ * some observed values for the 4 bytes preceding the formula: C0 5E 86 03
+ * C0 11 AC 02 80 F1 8A 03 D4 F0 8A 03
+ */
+ private int _unknownPreFormulaInt;
+ /** expect tRef, tRef3D, tArea, tArea3D or tName */
+ private Ptg _linkRefPtg;
+ /**
+ * Not clear if needed . Excel seems to be OK if this byte is not present.
+ * Value is often the same as the earlier firstColumn byte. */
+ private Byte _unknownPostFormulaByte;
+
+ public TextObjectRecord() {
+ }
+
+ public TextObjectRecord(RecordInputStream in) {
+ field_1_options = in.readUShort();
+ field_2_textOrientation = in.readUShort();
+ field_3_reserved4 = in.readUShort();
+ field_4_reserved5 = in.readUShort();
+ field_5_reserved6 = in.readUShort();
+ int field_6_textLength = in.readUShort();
+ int field_7_formattingDataLength = in.readUShort();
+ field_8_reserved7 = in.readInt();
+
+ if (in.remaining() > 0) {
+ // Text Objects can have simple reference formulas
+ // (This bit not mentioned in the MS document)
+ if (in.remaining() < 11) {
+ throw new RecordFormatException("Not enough remaining data for a link formula");
+ }
+ int formulaSize = in.readUShort();
+ _unknownPreFormulaInt = in.readInt();
+ Ptg[] ptgs = Ptg.readTokens(formulaSize, in);
+ if (ptgs.length != 1) {
+ throw new RecordFormatException("Read " + ptgs.length
+ + " tokens but expected exactly 1");
+ }
+ _linkRefPtg = ptgs[0];
+ if (in.remaining() > 0) {
+ _unknownPostFormulaByte = new Byte(in.readByte());
+ } else {
+ _unknownPostFormulaByte = null;
+ }
+ } else {
+ _linkRefPtg = null;
+ }
+ if (in.remaining() > 0) {
+ throw new RecordFormatException("Unused " + in.remaining() + " bytes at end of record");
+ }
+
+ String text;
+ if (field_6_textLength > 0) {
+ text = readRawString(in, field_6_textLength);
+ } else {
+ text = "";
+ }
+ _text = new HSSFRichTextString(text);
+
+ if (field_7_formattingDataLength > 0) {
+ if (in.isContinueNext() && in.remaining() == 0) {
+ in.nextRecord();
+ processFontRuns(in, _text, field_7_formattingDataLength);
+ } else {
+ throw new RecordFormatException(
+ "Expected Continue Record to hold font runs for TextObjectRecord");
+ }
+ }
+ }
+
+ private static String readRawString(RecordInputStream in, int textLength) {
+ byte compressByte = in.readByte();
+ boolean isCompressed = (compressByte & 0x01) == 0;
+ if (isCompressed) {
+ return in.readCompressedUnicode(textLength);
+ }
+ return in.readUnicodeLEString(textLength);
+ }
+
+ private static void processFontRuns(RecordInputStream in, HSSFRichTextString str,
+ int formattingRunDataLength) {
+ if (formattingRunDataLength % FORMAT_RUN_ENCODED_SIZE != 0) {
+ throw new RecordFormatException("Bad format run data length " + formattingRunDataLength
+ + ")");
+ }
+ if (in.remaining() != formattingRunDataLength) {
+ throw new RecordFormatException("Expected " + formattingRunDataLength
+ + " bytes but got " + in.remaining());
+ }
+ int nRuns = formattingRunDataLength / FORMAT_RUN_ENCODED_SIZE;
+ for (int i = 0; i < nRuns; i++) {
+ short index = in.readShort();
+ short iFont = in.readShort();
+ in.readInt(); // skip reserved.
+ str.applyFont(index, str.length(), iFont);
+ }
+ }
+
+ public short getSid() {
+ return sid;
+ }
+
+ /**
+ * Only for the current record. does not include any subsequent Continue
+ * records
+ */
+ private int getDataSize() {
+ int result = 2 + 2 + 2 + 2 + 2 + 2 + 2 + 4;
+ if (_linkRefPtg != null) {
+ result += 2 // formula size
+ + 4 // unknownInt
+ +_linkRefPtg.getSize();
+ if (_unknownPostFormulaByte != null) {
+ result += 1;
+ }
+ }
+ return result;
+ }
+
+ private int serializeTXORecord(int offset, byte[] data) {
+ int dataSize = getDataSize();
+
+ LittleEndian.putUShort(data, 0 + offset, TextObjectRecord.sid);
+ LittleEndian.putUShort(data, 2 + offset, dataSize);
+
+
+ LittleEndian.putUShort(data, 4 + offset, field_1_options);
+ LittleEndian.putUShort(data, 6 + offset, field_2_textOrientation);
+ LittleEndian.putUShort(data, 8 + offset, field_3_reserved4);
+ LittleEndian.putUShort(data, 10 + offset, field_4_reserved5);
+ LittleEndian.putUShort(data, 12 + offset, field_5_reserved6);
+ LittleEndian.putUShort(data, 14 + offset, _text.length());
+ LittleEndian.putUShort(data, 16 + offset, getFormattingDataLength());
+ LittleEndian.putInt(data, 18 + offset, field_8_reserved7);
+
+ if (_linkRefPtg != null) {
+ int pos = offset+22;
+ int formulaSize = _linkRefPtg.getSize();
+ LittleEndian.putUShort(data, pos, formulaSize);
+ pos += LittleEndian.SHORT_SIZE;
+ LittleEndian.putInt(data, pos, _unknownPreFormulaInt);
+ pos += LittleEndian.INT_SIZE;
+ _linkRefPtg.writeBytes(data, pos);
+ pos += formulaSize;
+ if (_unknownPostFormulaByte != null) {
+ LittleEndian.putByte(data, pos, _unknownPostFormulaByte.byteValue());
+ pos += LittleEndian.BYTE_SIZE;
+ }
+ }
+
+ return 4 + dataSize;
+ }
+
+ private int serializeTrailingRecords(int offset, byte[] data) {
+ byte[] textBytes;
+ try {
+ textBytes = _text.getString().getBytes("UTF-16LE");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ int remainingLength = textBytes.length;
+
+ int countTextBytesWritten = 0;
+ int pos = offset;
+ // (regardless what was read, we always serialize double-byte
+ // unicode characters (UTF-16LE).
+ Byte unicodeFlag = new Byte((byte)1);
+ while (remainingLength > 0) {
+ int chunkSize = Math.min(RecordInputStream.MAX_RECORD_DATA_SIZE - 2, remainingLength);
+ remainingLength -= chunkSize;
+ pos += ContinueRecord.write(data, pos, unicodeFlag, textBytes, countTextBytesWritten, chunkSize);
+ countTextBytesWritten += chunkSize;
+ }
+
+ byte[] formatData = createFormatData(_text);
+ pos += ContinueRecord.write(data, pos, null, formatData);
+ return pos - offset;
+ }
+
+ private int getTrailingRecordsSize() {
+ if (_text.length() < 1) {
+ return 0;
+ }
+ int encodedTextSize = 0;
+ int textBytesLength = _text.length() * LittleEndian.SHORT_SIZE;
+ while (textBytesLength > 0) {
+ int chunkSize = Math.min(RecordInputStream.MAX_RECORD_DATA_SIZE - 2, textBytesLength);
+ textBytesLength -= chunkSize;
+
+ encodedTextSize += 4; // +4 for ContinueRecord sid+size
+ encodedTextSize += 1+chunkSize; // +1 for compressed unicode flag,
+ }
+
+ int encodedFormatSize = (_text.numFormattingRuns() + 1) * FORMAT_RUN_ENCODED_SIZE
+ + 4; // +4 for ContinueRecord sid+size
+ return encodedTextSize + encodedFormatSize;
+ }
+
+
+ public int serialize(int offset, byte[] data) {
+
+ int expectedTotalSize = getRecordSize();
+ int totalSize = serializeTXORecord(offset, data);
+
+ if (_text.getString().length() > 0) {
+ totalSize += serializeTrailingRecords(offset+totalSize, data);
+ }
+
+ if (totalSize != expectedTotalSize)
+ throw new RecordFormatException(totalSize
+ + " bytes written but getRecordSize() reports " + expectedTotalSize);
+ return totalSize;
+ }
+
+ /**
+ * Note - this total size includes all potential {@link ContinueRecord}s written
+ */
+ public int getRecordSize() {
+ int baseSize = 4 + getDataSize();
+ return baseSize + getTrailingRecordsSize();
+ }
+
+
+ private int getFormattingDataLength() {
+ if (_text.length() < 1) {
+ // important - no formatting data if text is empty
+ return 0;
+ }
+ return (_text.numFormattingRuns() + 1) * FORMAT_RUN_ENCODED_SIZE;
+ }
+
+ private static byte[] createFormatData(HSSFRichTextString str) {
+ int nRuns = str.numFormattingRuns();
+ byte[] result = new byte[(nRuns + 1) * FORMAT_RUN_ENCODED_SIZE];
+ int pos = 0;
+ for (int i = 0; i < nRuns; i++) {
+ LittleEndian.putUShort(result, pos, str.getIndexOfFormattingRun(i));
+ pos += 2;
+ int fontIndex = str.getFontOfFormattingRun(i);
+ LittleEndian.putUShort(result, pos, fontIndex == str.NO_FONT ? 0 : fontIndex);
+ pos += 2;
+ pos += 4; // skip reserved
+ }
+ LittleEndian.putUShort(result, pos, str.length());
+ pos += 2;
+ LittleEndian.putUShort(result, pos, 0);
+ pos += 2;
+ pos += 4; // skip reserved
+
+ return result;
+ }
+
+ /**
+ * Sets the Horizontal text alignment field value.
+ */
+ public void setHorizontalTextAlignment(int value) {
+ field_1_options = HorizontalTextAlignment.setValue(field_1_options, value);
+ }
+
+ /**
+ * @return the Horizontal text alignment field value.
+ */
+ public int getHorizontalTextAlignment() {
+ return HorizontalTextAlignment.getValue(field_1_options);
+ }
+
+ /**
+ * Sets the Vertical text alignment field value.
+ */
+ public void setVerticalTextAlignment(int value) {
+ field_1_options = VerticalTextAlignment.setValue(field_1_options, value);
+ }
+
+ /**
+ * @return the Vertical text alignment field value.
+ */
+ public int getVerticalTextAlignment() {
+ return VerticalTextAlignment.getValue(field_1_options);
+ }
+
+ /**
+ * Sets the text locked field value.
+ */
+ public void setTextLocked(boolean value) {
+ field_1_options = textLocked.setBoolean(field_1_options, value);
+ }
+
+ /**
+ * @return the text locked field value.
+ */
+ public boolean isTextLocked() {
+ return textLocked.isSet(field_1_options);
+ }
+
+ /**
+ * Get the text orientation field for the TextObjectBase record.
+ *
+ * @return One of TEXT_ORIENTATION_NONE TEXT_ORIENTATION_TOP_TO_BOTTOM
+ * TEXT_ORIENTATION_ROT_RIGHT TEXT_ORIENTATION_ROT_LEFT
+ */
+ public int getTextOrientation() {
+ return field_2_textOrientation;
+ }
+
+ /**
+ * Set the text orientation field for the TextObjectBase record.
+ *
+ * @param textOrientation
+ * One of TEXT_ORIENTATION_NONE TEXT_ORIENTATION_TOP_TO_BOTTOM
+ * TEXT_ORIENTATION_ROT_RIGHT TEXT_ORIENTATION_ROT_LEFT
+ */
+ public void setTextOrientation(int textOrientation) {
+ this.field_2_textOrientation = textOrientation;
+ }
+
+ public HSSFRichTextString getStr() {
+ return _text;
+ }
+
+ public void setStr(HSSFRichTextString str) {
+ _text = str;
+ }
+
+ public Ptg getLinkRefPtg() {
+ return _linkRefPtg;
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("[TXO]\n");
+ sb.append(" .options = ").append(HexDump.shortToHex(field_1_options)).append("\n");
+ sb.append(" .isHorizontal = ").append(getHorizontalTextAlignment()).append('\n');
+ sb.append(" .isVertical = ").append(getVerticalTextAlignment()).append('\n');
+ sb.append(" .textLocked = ").append(isTextLocked()).append('\n');
+ sb.append(" .textOrientation= ").append(HexDump.shortToHex(getTextOrientation())).append("\n");
+ sb.append(" .reserved4 = ").append(HexDump.shortToHex(field_3_reserved4)).append("\n");
+ sb.append(" .reserved5 = ").append(HexDump.shortToHex(field_4_reserved5)).append("\n");
+ sb.append(" .reserved6 = ").append(HexDump.shortToHex(field_5_reserved6)).append("\n");
+ sb.append(" .textLength = ").append(HexDump.shortToHex(_text.length())).append("\n");
+ sb.append(" .reserved7 = ").append(HexDump.intToHex(field_8_reserved7)).append("\n");
+
+ sb.append(" .string = ").append(_text).append('\n');
+
+ for (int i = 0; i < _text.numFormattingRuns(); i++) {
+ sb.append(" .textrun = ").append(_text.getFontOfFormattingRun(i)).append('\n');
+
+ }
+ sb.append("[/TXO]\n");
+ return sb.toString();
+ }
+
+ public Object clone() {
+
+ TextObjectRecord rec = new TextObjectRecord();
+ rec._text = _text;
+
+ rec.field_1_options = field_1_options;
+ rec.field_2_textOrientation = field_2_textOrientation;
+ rec.field_3_reserved4 = field_3_reserved4;
+ rec.field_4_reserved5 = field_4_reserved5;
+ rec.field_5_reserved6 = field_5_reserved6;
+ rec.field_8_reserved7 = field_8_reserved7;
+
+ rec._text = _text; // clone needed?
+
+ if (_linkRefPtg != null) {
+ rec._unknownPreFormulaInt = _unknownPreFormulaInt;
+ rec._linkRefPtg = _linkRefPtg.copy();
+ rec._unknownPostFormulaByte = rec._unknownPostFormulaByte;
+ }
+ return rec;
+ }
}
if (string.numFormattingRuns() == 0) string.applyFont((short)0);
if (txo != null) {
- int frLength = ( string.numFormattingRuns() + 1 ) * 8;
- txo.setFormattingRunLength( (short) frLength );
- txo.setTextLength( (short) string.length() );
- txo.setStr( string );
+ txo.setStr(string);
}
super.setString(string);
}
/**
- * @return the number of characters in the font.
+ * @return the number of characters in the text.
*/
public int length()
{
package org.apache.poi.hssf.record;
+import java.io.ByteArrayInputStream;
+
+import org.apache.poi.hssf.usermodel.HSSFRichTextString;
+import org.apache.poi.util.HexRead;
+
import junit.framework.TestCase;
/**
* class works correctly. Test data taken directly from a real
* Excel file.
*
-
* @author Glen Stampoultzis (glens at apache.org)
*/
-public class TestTextObjectBaseRecord extends TestCase {
- byte[] data = new byte[] {
- 0x44, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x00, 0x00,
- };
+public final class TestTextObjectBaseRecord extends TestCase {
+ /** data for one TXO rec and two continue recs */
+ private static final byte[] data = HexRead.readFromString(
+ "B6 01 " + // TextObjectRecord.sid
+ "12 00 " + // size 18
+ "44 02 02 00 00 00 00 00" +
+ "00 00 " +
+ "02 00 " + // strLen 2
+ "10 00 " + // 16 bytes for 2 format runs
+ "00 00" +
+ "00 00 " +
+ "3C 00 " + // ContinueRecord.sid
+ "05 00 " + // size 5
+ "01 " + // unicode uncompressed
+ "41 00 42 00 " + // 'AB'
+ "3C 00 " + // ContinueRecord.sid
+ "10 00 " + // size 16
+ "00 00 18 00 00 00 00 00 " +
+ "02 00 00 00 00 00 00 00 "
+ );
+
public void testLoad() {
- TextObjectBaseRecord record = new TextObjectBaseRecord(new TestcaseRecordInputStream((short)0x1B6, (short)data.length, data));
-
-
-// assertEquals( (short), record.getOptions());
- assertEquals( false, record.isReserved1() );
- assertEquals( TextObjectBaseRecord.HORIZONTAL_TEXT_ALIGNMENT_CENTERED, record.getHorizontalTextAlignment() );
- assertEquals( TextObjectBaseRecord.VERTICAL_TEXT_ALIGNMENT_JUSTIFY, record.getVerticalTextAlignment() );
- assertEquals( 0, record.getReserved2() );
- assertEquals( true, record.isTextLocked() );
- assertEquals( 0, record.getReserved3() );
- assertEquals( TextObjectBaseRecord.TEXT_ORIENTATION_ROT_RIGHT, record.getTextOrientation());
- assertEquals( 0, record.getReserved4());
- assertEquals( 0, record.getReserved5());
- assertEquals( 0, record.getReserved6());
- assertEquals( 2, record.getTextLength());
- assertEquals( 2, record.getFormattingRunLength());
- assertEquals( 0, record.getReserved7());
-
-
- assertEquals( 22, record.getRecordSize() );
+ RecordInputStream in = new RecordInputStream(new ByteArrayInputStream(data));
+ in.nextRecord();
+ TextObjectRecord record = new TextObjectRecord(in);
+
+
+ assertEquals(TextObjectRecord.HORIZONTAL_TEXT_ALIGNMENT_CENTERED, record.getHorizontalTextAlignment());
+ assertEquals(TextObjectRecord.VERTICAL_TEXT_ALIGNMENT_JUSTIFY, record.getVerticalTextAlignment());
+ assertEquals(true, record.isTextLocked());
+ assertEquals(TextObjectRecord.TEXT_ORIENTATION_ROT_RIGHT, record.getTextOrientation());
+
+ assertEquals(51, record.getRecordSize() );
}
public void testStore()
{
- TextObjectBaseRecord record = new TextObjectBaseRecord();
+ TextObjectRecord record = new TextObjectRecord();
+ HSSFRichTextString str = new HSSFRichTextString("AB");
+ str.applyFont(0, 2, (short)0x0018);
+ str.applyFont(2, 2, (short)0x0320);
-// record.setOptions( (short) 0x0000);
- record.setReserved1( false );
- record.setHorizontalTextAlignment( TextObjectBaseRecord.HORIZONTAL_TEXT_ALIGNMENT_CENTERED );
- record.setVerticalTextAlignment( TextObjectBaseRecord.VERTICAL_TEXT_ALIGNMENT_JUSTIFY );
- record.setReserved2( (short)0 );
- record.setTextLocked( true );
- record.setReserved3( (short)0 );
- record.setTextOrientation( TextObjectBaseRecord.TEXT_ORIENTATION_ROT_RIGHT );
- record.setReserved4( (short)0 );
- record.setReserved5( (short)0 );
- record.setReserved6( (short)0 );
- record.setTextLength( (short)2 );
- record.setFormattingRunLength( (short)2 );
- record.setReserved7( 0 );
+ record.setHorizontalTextAlignment(TextObjectRecord.HORIZONTAL_TEXT_ALIGNMENT_CENTERED);
+ record.setVerticalTextAlignment(TextObjectRecord.VERTICAL_TEXT_ALIGNMENT_JUSTIFY);
+ record.setTextLocked(true);
+ record.setTextOrientation(TextObjectRecord.TEXT_ORIENTATION_ROT_RIGHT);
+ record.setStr(str);
byte [] recordBytes = record.serialize();
- assertEquals(recordBytes.length - 4, data.length);
+ assertEquals(recordBytes.length, data.length);
for (int i = 0; i < data.length; i++)
- assertEquals("At offset " + i, data[i], recordBytes[i+4]);
+ assertEquals("At offset " + i, data[i], recordBytes[i]);
}
}
\r
import junit.framework.TestCase;\r
\r
+import org.apache.poi.hssf.record.formula.Ptg;\r
+import org.apache.poi.hssf.record.formula.RefPtg;\r
import org.apache.poi.hssf.usermodel.HSSFRichTextString;\r
+import org.apache.poi.util.HexRead;\r
+import org.apache.poi.util.LittleEndian;\r
\r
/**\r
* Tests that serialization and deserialization of the TextObjectRecord .\r
*/\r
public final class TestTextObjectRecord extends TestCase {\r
\r
- byte[] data = {(byte)0xB6, 0x01, 0x12, 0x00, 0x12, 0x02, 0x00, 0x00, 0x00, 0x00,\r
- 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,\r
- 0x00, 0x3C, 0x00, 0x1B, 0x00, 0x01, 0x48, 0x00, 0x65, 0x00, 0x6C,\r
- 0x00, 0x6C, 0x00, 0x6F, 0x00, 0x2C, 0x00, 0x20, 0x00, 0x57, 0x00,\r
- 0x6F, 0x00, 0x72, 0x00, 0x6C, 0x00, 0x64, 0x00, 0x21, 0x00, 0x3C,\r
- 0x00, 0x08, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };\r
+ private static final byte[] simpleData = HexRead.readFromString(\r
+ "B6 01 12 00 " +\r
+ "12 02 00 00 00 00 00 00" +\r
+ "00 00 0D 00 08 00 00 00" +\r
+ "00 00 " +\r
+ "3C 00 1B 00 " +\r
+ "01 48 00 65 00 6C 00 6C 00 6F 00 " +\r
+ "2C 00 20 00 57 00 6F 00 72 00 6C " +\r
+ "00 64 00 21 00 " + \r
+ "3C 00 08 " +\r
+ "00 0D 00 00 00 00 00 00 00"\r
+ );\r
\r
\r
public void testRead() {\r
\r
- RecordInputStream is = new RecordInputStream(new ByteArrayInputStream(data));\r
+ RecordInputStream is = new RecordInputStream(new ByteArrayInputStream(simpleData));\r
is.nextRecord();\r
TextObjectRecord record = new TextObjectRecord(is);\r
\r
assertEquals(TextObjectRecord.HORIZONTAL_TEXT_ALIGNMENT_LEFT_ALIGNED, record.getHorizontalTextAlignment());\r
assertEquals(TextObjectRecord.VERTICAL_TEXT_ALIGNMENT_TOP, record.getVerticalTextAlignment());\r
assertEquals(TextObjectRecord.TEXT_ORIENTATION_NONE, record.getTextOrientation());\r
- assertEquals(0, record.getReserved7());\r
assertEquals("Hello, World!", record.getStr().getString());\r
-\r
}\r
\r
- public void testWrite()\r
- {\r
+ public void testWrite() {\r
HSSFRichTextString str = new HSSFRichTextString("Hello, World!");\r
\r
TextObjectRecord record = new TextObjectRecord();\r
- int frLength = ( str.numFormattingRuns() + 1 ) * 8;\r
- record.setFormattingRunLength( (short) frLength );\r
- record.setTextLength( (short) str.length() );\r
- record.setStr( str );\r
+ record.setStr(str);\r
record.setHorizontalTextAlignment( TextObjectRecord.HORIZONTAL_TEXT_ALIGNMENT_LEFT_ALIGNED );\r
record.setVerticalTextAlignment( TextObjectRecord.VERTICAL_TEXT_ALIGNMENT_TOP );\r
record.setTextLocked( true );\r
record.setTextOrientation( TextObjectRecord.TEXT_ORIENTATION_NONE );\r
- record.setReserved7( 0 );\r
\r
byte [] ser = record.serialize();\r
- //assertEquals(ser.length , data.length);\r
+ assertEquals(ser.length , simpleData.length);\r
\r
- //assertTrue(Arrays.equals(data, ser));\r
+ assertTrue(Arrays.equals(simpleData, ser));\r
\r
//read again\r
- RecordInputStream is = new RecordInputStream(new ByteArrayInputStream(data));\r
+ RecordInputStream is = new RecordInputStream(new ByteArrayInputStream(simpleData));\r
is.nextRecord();\r
record = new TextObjectRecord(is);\r
+ }\r
+\r
+ /**\r
+ * Zero {@link ContinueRecord}s follow a {@link TextObjectRecord} if the text is empty\r
+ */\r
+ public void testWriteEmpty() {\r
+ HSSFRichTextString str = new HSSFRichTextString("");\r
+\r
+ TextObjectRecord record = new TextObjectRecord();\r
+ record.setStr(str);\r
\r
+ byte [] ser = record.serialize();\r
+ \r
+ int formatDataLen = LittleEndian.getUShort(ser, 16);\r
+ assertEquals("formatDataLength", 0, formatDataLen);\r
+\r
+ assertEquals(22, ser.length); // just the TXO record\r
+ \r
+ //read again\r
+ RecordInputStream is = new RecordInputStream(new ByteArrayInputStream(ser));\r
+ is.nextRecord();\r
+ record = new TextObjectRecord(is);\r
+ assertEquals(0, record.getStr().length());\r
}\r
\r
/**\r
HSSFRichTextString str = new HSSFRichTextString(buff.toString());\r
\r
TextObjectRecord obj = new TextObjectRecord();\r
- int frLength = ( str.numFormattingRuns() + 1 ) * 8;\r
- obj.setFormattingRunLength( (short) frLength );\r
- obj.setTextLength( (short) str.length() );\r
- obj.setStr( str );\r
+ obj.setStr(str);\r
\r
byte [] data = obj.serialize();\r
RecordInputStream is = new RecordInputStream(new ByteArrayInputStream(data));\r
HSSFRichTextString str = new HSSFRichTextString(text);\r
\r
TextObjectRecord obj = new TextObjectRecord();\r
- int frLength = ( str.numFormattingRuns() + 1 ) * 8;\r
- obj.setFormattingRunLength( (short) frLength );\r
- obj.setTextLength( (short) str.length() );\r
- obj.setReserved1(true);\r
- obj.setReserved2((short)2);\r
- obj.setReserved3((short)3);\r
- obj.setReserved4((short)4);\r
- obj.setReserved5((short)5);\r
- obj.setReserved6((short)6);\r
- obj.setReserved7((short)7);\r
obj.setStr( str );\r
\r
\r
TextObjectRecord cloned = (TextObjectRecord)obj.clone();\r
- assertEquals(obj.getReserved2(), cloned.getReserved2());\r
- assertEquals(obj.getReserved3(), cloned.getReserved3());\r
- assertEquals(obj.getReserved4(), cloned.getReserved4());\r
- assertEquals(obj.getReserved5(), cloned.getReserved5());\r
- assertEquals(obj.getReserved6(), cloned.getReserved6());\r
- assertEquals(obj.getReserved7(), cloned.getReserved7());\r
assertEquals(obj.getRecordSize(), cloned.getRecordSize());\r
- assertEquals(obj.getOptions(), cloned.getOptions());\r
assertEquals(obj.getHorizontalTextAlignment(), cloned.getHorizontalTextAlignment());\r
- assertEquals(obj.getFormattingRunLength(), cloned.getFormattingRunLength());\r
assertEquals(obj.getStr().getString(), cloned.getStr().getString());\r
\r
//finally check that the serialized data is the same\r
byte[] cln = cloned.serialize();\r
assertTrue(Arrays.equals(src, cln));\r
}\r
+ \r
+ /** similar to {@link #simpleData} but with link formula at end of TXO rec*/ \r
+ private static final byte[] linkData = HexRead.readFromString(\r
+ "B6 01 " + // TextObjectRecord.sid\r
+ "1E 00 " + // size 18\r
+ "44 02 02 00 00 00 00 00" +\r
+ "00 00 " +\r
+ "02 00 " + // strLen 2\r
+ "10 00 " + // 16 bytes for 2 format runs\r
+ "00 00 00 00 " +\r
+\r
+ "05 00 " + // formula size\r
+ "D4 F0 8A 03 " + // unknownInt\r
+ "24 01 00 13 C0 " + //tRef(T2)\r
+ "13 " + // ??\r
+\r
+ "3C 00 " + // ContinueRecord.sid\r
+ "05 00 " + // size 5\r
+ "01 " + // unicode uncompressed\r
+ "41 00 42 00 " + // 'AB'\r
+ "3C 00 " + // ContinueRecord.sid\r
+ "10 00 " + // size 16 \r
+ "00 00 18 00 00 00 00 00 " +\r
+ "02 00 00 00 00 00 00 00 " \r
+ );\r
+ \r
+ \r
+ public void testLinkFormula() {\r
+ RecordInputStream is = new RecordInputStream(new ByteArrayInputStream(linkData));\r
+ is.nextRecord();\r
+ TextObjectRecord rec = new TextObjectRecord(is);\r
+ \r
+ Ptg ptg = rec.getLinkRefPtg();\r
+ assertNotNull(ptg);\r
+ assertEquals(RefPtg.class, ptg.getClass());\r
+ RefPtg rptg = (RefPtg) ptg;\r
+ assertEquals("T2", rptg.toFormulaString());\r
+\r
+ byte [] data2 = rec.serialize();\r
+ assertEquals(linkData.length, data2.length);\r
+ assertTrue(Arrays.equals(linkData, data2));\r
+ }\r
+ \r
}\r